diff --git a/apps/llm/adapters.py b/apps/llm/adapters.py index 5343cbcef6a297013eaedfa056653924938d3c84..45ffe4f678a2349e8782429674c07fd9017b1489 100644 --- a/apps/llm/adapters.py +++ b/apps/llm/adapters.py @@ -261,7 +261,17 @@ class AdapterFactoryV2: def get_provider_from_endpoint(endpoint: str) -> str: - """从endpoint推断provider""" + """从endpoint推断provider + + ⚠️ 注意:这个函数应该只作为后备方案使用! + 优先级应该是:明确的provider参数 > 从endpoint推断 + + Args: + endpoint: API endpoint URL + + Returns: + 推断出的provider名称,如果无法推断则返回"unknown" + """ if not endpoint: return "unknown" @@ -272,7 +282,7 @@ def get_provider_from_endpoint(endpoint: str) -> str: elif "siliconflow.cn" in endpoint_lower: return "siliconflow" elif "dashscope.aliyuncs.com" in endpoint_lower: - return "qwen" + return "bailian" elif "api.deepseek.com" in endpoint_lower: return "deepseek" elif "baichuan-ai.com" in endpoint_lower: diff --git a/apps/llm/function.py b/apps/llm/function.py index 71499a9eb3ee9554f7e92b9f427489a36195b3a9..7970ef9421e37b8c808952e3f2fb7a8749778b91 100644 --- a/apps/llm/function.py +++ b/apps/llm/function.py @@ -21,10 +21,49 @@ from apps.llm.adapters import AdapterFactory, get_provider_from_endpoint import openai import httpx from openai import APIError, APIConnectionError, APITimeoutError, RateLimitError, AuthenticationError +from apps.llm.model_registry import model_registry +from apps.llm.model_types import ModelType, ChatCapabilities logger = logging.getLogger(__name__) +def infer_backend_from_capabilities(provider: str, model_name: str, explicit_backend: str | None = None) -> str: + """ + 根据模型能力推断最佳的backend + + :param provider: 模型提供商 + :param model_name: 模型名称 + :param explicit_backend: 明确指定的backend(如果有),优先使用 + :return: 推断的backend类型 + """ + # 如果明确指定了backend,直接使用 + if explicit_backend: + logger.info(f"[FunctionCall] 使用明确指定的backend: {explicit_backend}") + return explicit_backend + + # 从模型注册表获取模型能力 + capabilities = model_registry.get_model_capabilities(provider, model_name, ModelType.CHAT) + + if not capabilities or not isinstance(capabilities, ChatCapabilities): + logger.warning(f"[FunctionCall] 无法获取模型 {provider}:{model_name} 的能力信息,使用默认backend: json_mode") + return "json_mode" + + # 根据能力优先级推断backend + # 优先级: structured_output > function_call > json_mode + if capabilities.supports_structured_output: + logger.info(f"[FunctionCall] 模型 {provider}:{model_name} 支持structured_output,自动选择") + return "structured_output" + elif capabilities.supports_function_calling: + logger.info(f"[FunctionCall] 模型 {provider}:{model_name} 支持function_calling,自动选择") + return "function_call" + elif capabilities.supports_json_mode: + logger.info(f"[FunctionCall] 模型 {provider}:{model_name} 支持json_mode,自动选择") + return "json_mode" + else: + logger.warning(f"[FunctionCall] 模型 {provider}:{model_name} 不支持任何JSON生成能力,回退到json_mode") + return "json_mode" + + class FunctionLLM: """用于FunctionCall的模型""" @@ -39,6 +78,8 @@ class FunctionLLM: - json_mode - structured_output + backend会根据模型能力自动推断,也可以通过配置明确指定 + :param llm_config: 可选的LLM配置,如果不提供则使用配置文件中的function_call配置 """ # 使用传入的配置或从配置文件获取 @@ -46,7 +87,7 @@ class FunctionLLM: self._config = llm_config # 如果没有backend字段,根据模型特性推断 if not hasattr(self._config, 'backend'): - # 默认使用json_mode,这对大多数模型都适用 + # 创建一个包含backend的配置对象 class ConfigWithBackend: def __init__(self, base_config): self.model = base_config.model @@ -54,7 +95,8 @@ class FunctionLLM: self.api_key = getattr(base_config, 'key', getattr(base_config, 'api_key', '')) self.max_tokens = getattr(base_config, 'max_tokens', 8192) self.temperature = getattr(base_config, 'temperature', 0.7) - self.backend = "json_mode" # 默认使用json_mode + # backend将在后面通过推断设置 + self.backend = None self._config = ConfigWithBackend(llm_config) else: @@ -66,18 +108,37 @@ class FunctionLLM: logger.error(err_msg) raise ValueError(err_msg) - # 初始化适配器 - self._provider = get_provider_from_endpoint(self._config.endpoint) + # 初始化provider和适配器 + # 优先使用配置中的provider,如果没有则从endpoint推断 + if hasattr(self._config, 'provider') and self._config.provider: + self._provider = self._config.provider + else: + self._provider = get_provider_from_endpoint(self._config.endpoint) + self._adapter = AdapterFactory.create_adapter(self._provider, self._config.model) + # 智能推断backend:如果配置中backend为None或空字符串,则根据模型能力推断 + explicit_backend = getattr(self._config, 'backend', None) + if not explicit_backend or explicit_backend == 'null': + explicit_backend = None + + self._backend = infer_backend_from_capabilities( + self._provider, + self._config.model, + explicit_backend + ) + + # 更新配置对象的backend字段,供后续使用 + self._config.backend = self._backend + self._params = { "model": self._config.model, "messages": [], "extra_body": {} } - if self._config.backend != "ollama": + if self._backend != "ollama": self._params["timeout"] = 300 - if self._config.backend == "ollama": + if self._backend == "ollama": import ollama if not self._config.api_key: @@ -127,23 +188,15 @@ class FunctionLLM: "temperature": temperature, }) - # 🔑 特殊处理:如果provider是siliconflow,强制使用json_mode - if self._provider == "siliconflow": - logger.info("[FunctionCall] 检测到SiliconFlow provider,启用JSON模式") - # 确保系统消息中包含JSON输出提示 - if messages and messages[0]["role"] == "system": - if "JSON" not in messages[0]["content"]: - messages[0]["content"] += "\nYou are a helpful assistant designed to output JSON." - self._params["response_format"] = {"type": "json_object"} - - elif self._config.backend == "vllm": + # 根据backend配置选择不同的调用方式 + if self._backend == "vllm": self._params["extra_body"] = {"guided_json": schema} - elif self._config.backend == "json_mode": + elif self._backend == "json_mode": logger.warning("[FunctionCall] json_mode无法确保输出格式符合要求,使用效果将受到影响") self._params["response_format"] = {"type": "json_object"} - elif self._config.backend == "structured_output": + elif self._backend == "structured_output": self._params["response_format"] = { "type": "json_schema", "json_schema": { @@ -154,7 +207,7 @@ class FunctionLLM: }, } - elif self._config.backend == "function_call": + elif self._backend == "function_call": logger.warning("[FunctionCall] function_call无法确保一定调用工具,使用效果将受到影响") self._params["tools"] = [ { @@ -327,12 +380,12 @@ class FunctionLLM: temperature = self._config.temperature try: - if self._config.backend == "ollama": + if self._backend == "ollama": json_str = await self._call_ollama(messages, schema, max_tokens, temperature) - elif self._config.backend in ["function_call", "json_mode", "response_format", "vllm", "structured_output"]: + elif self._backend in ["function_call", "json_mode", "response_format", "vllm", "structured_output"]: json_str = await self._call_openai(messages, schema, max_tokens, temperature) else: - err = f"未知的Function模型后端: {self._config.backend}" + err = f"未知的Function模型后端: {self._backend}" logger.error("[FunctionCall] %s", err) raise ValueError(err) except ValueError: @@ -451,8 +504,10 @@ class JsonGenerator: async def _assemble_message(self) -> str: """组装消息""" - # 检查类型 - function_call = Config().get_config().function_call.backend == "function_call" + # 创建一个临时FunctionLLM实例来获取backend信息 + # 注意:这里只是为了检查backend类型,不会真正调用 + temp_function = FunctionLLM() + function_call = temp_function._backend == "function_call" # 渲染模板 template = self._env.from_string(JSON_GEN_BASIC) diff --git a/apps/llm/model_registry.py b/apps/llm/model_registry.py index 45b49b19f72f8e08310e8de2447aab22cbaa7b39..a4ee0e712748dad7da0b3eefe3a718327cf9dd25 100644 --- a/apps/llm/model_registry.py +++ b/apps/llm/model_registry.py @@ -510,14 +510,22 @@ def get_model_can_toggle_thinking(provider: str, model_name: str, registry: Opti # 初始化全局注册表实例 def initialize_global_registry(providers_path: Optional[str] = None, models_path: Optional[str] = None) -> ModelRegistryV2: """初始化全局注册表""" + import os + # 默认配置路径 if providers_path is None: - base_dir = Path(__file__).parent.parent.parent / "deploy" / "chart" / "euler_copilot" / "configs" / "framework" - providers_path = str(base_dir / "providers.conf") + # 优先从环境变量PROVIDERS_CONFIG读取 + providers_path = os.getenv("PROVIDERS_CONFIG") + if providers_path is None: + # 默认使用 Path(__file__).parents[2] / "config" / "providers.conf" + providers_path = str(Path(__file__).parents[2] / "config" / "providers.conf") if models_path is None: - base_dir = Path(__file__).parent.parent.parent / "deploy" / "chart" / "euler_copilot" / "configs" / "framework" - models_path = str(base_dir / "models.conf") + # 优先从环境变量MODELS_CONFIG读取 + models_path = os.getenv("MODELS_CONFIG") + if models_path is None: + # 默认使用 Path(__file__).parents[2] / "config" / "models.conf" + models_path = str(Path(__file__).parents[2] / "config" / "models.conf") registry = ModelRegistryV2(providers_path, models_path) return registry diff --git a/apps/llm/reasoning.py b/apps/llm/reasoning.py index ed7dc94e3c843c527c816fb8a50d2f0c461620ae..14649b8d0e05cb09a27a699fd7a22b0b5293ffc5 100644 --- a/apps/llm/reasoning.py +++ b/apps/llm/reasoning.py @@ -137,7 +137,11 @@ class ReasoningLLM: self._init_client() # 初始化适配器 - self._provider = get_provider_from_endpoint(self._config.endpoint) + # 优先使用配置中的provider,如果没有则从endpoint推断 + if hasattr(self._config, 'provider') and self._config.provider: + self._provider = self._config.provider + else: + self._provider = get_provider_from_endpoint(self._config.endpoint) self._adapter = AdapterFactory.create_adapter(self._provider, self._config.model) def _init_client(self) -> None: diff --git a/apps/schemas/config.py b/apps/schemas/config.py index 8b97d08197a76e069cffd6c411dd9e4811a6a049..cf838a1773fd549a8a58da7781094cc3ed24aa8f 100644 --- a/apps/schemas/config.py +++ b/apps/schemas/config.py @@ -152,7 +152,6 @@ class LLMConfig(BaseModel): class FunctionCallConfig(BaseModel): """Function Call配置""" - backend: str = Field(description="Function Call 后端") provider: str | None = Field(default=None, description="Function Call 提供商") model: str = Field(description="Function Call 模型名") endpoint: str = Field(description="Function Call API URL地址") diff --git a/apps/services/document.py b/apps/services/document.py index 91726101525a7218075c0e72350b1c421b5caeda..5d4238583a03467797ba8a53bda20f0b24afb5ea 100644 --- a/apps/services/document.py +++ b/apps/services/document.py @@ -191,7 +191,7 @@ class DocumentManager: # 尝试清理MinIO中的文件 try: from apps.common.minio import MinioClient - MinioClient.remove_object("document", file_id) + MinioClient.delete_file("document", file_id) logger.info(f"已清理MinIO中的文件: {file_id}") except Exception: logger.warning(f"清理MinIO文件失败: {file_id}") @@ -228,7 +228,7 @@ class DocumentManager: for doc in uploaded_files: try: from apps.common.minio import MinioClient - MinioClient.remove_object("document", doc.id) + MinioClient.delete_file("document", doc.id) except Exception as cleanup_error: cleanup_errors.append(f"MinIO清理失败 {doc.id}: {cleanup_error}") @@ -470,7 +470,8 @@ class DocumentManager: name=final_var_name, value=updated_value, var_type=existing_variable.metadata.var_type, - description=existing_variable.metadata.description + description=existing_variable.metadata.description, + force_system_update=(scope == "system") # system.files 允许更新 ) logger.info(f"✅ 成功更新文件变量: {final_var_name}, uploaded_files_count={len(uploaded_files)}") except Exception as e: diff --git a/apps/services/llm.py b/apps/services/llm.py index 1d150491919701e061eef1081196311c9e4c537a..77e37b74e7b6d6725f6f3b10fce137a181940b21 100644 --- a/apps/services/llm.py +++ b/apps/services/llm.py @@ -180,6 +180,24 @@ class LLMManager: # 推断模型能力 provider = req.provider or get_provider_from_endpoint(req.openai_base_url) + + # 检查provider类型,如果是public类型,则验证URL + if provider in llm_provider_dict: + provider_info = llm_provider_dict[provider] + provider_type = provider_info.get("type", "public") + + # 如果是public类型的provider + if provider_type == "public": + standard_url = provider_info.get("url", "") + # 如果用户提供的URL不为空,且与标准URL不一致 + if req.openai_base_url and req.openai_base_url.rstrip('/') != standard_url.rstrip('/'): + err = f"[LLMManager] public类型的provider '{provider}' 不允许自定义URL,应使用标准URL: {standard_url}" + logger.error(err) + raise ValueError(err) + # 如果用户没有提供URL,使用标准URL + if not req.openai_base_url: + req.openai_base_url = standard_url + model_info = model_registry.get_model_info(provider, req.model_name) # 标准化type字段为列表格式 diff --git a/deploy/chart/euler_copilot/configs/framework/models.conf b/deploy/chart/euler_copilot/configs/framework/models.conf index 226319f38ffd77a729e959c046cbdbfc82b52e65..77fb0b60eefeafcce8c4fc83b122996abeccacb6 100644 --- a/deploy/chart/euler_copilot/configs/framework/models.conf +++ b/deploy/chart/euler_copilot/configs/framework/models.conf @@ -171,17 +171,17 @@ "notes": "多语言重排序模型" }, - "_comment_qwen": "=== 阿里百炼 Qwen ===", + "_comment_bailian": "=== 阿里百炼 Qwen ===", - "qwen:qwen-plus": { - "provider": "qwen", + "bailian:qwen-plus": { + "provider": "bailian", "model_name": "qwen-plus", "model_type": "chat", "series": "qwen-api", "display_name": "通义千问 Plus", "context_window": 131072, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": true, "can_toggle_thinking": true, "supports_enable_thinking": true, @@ -190,15 +190,15 @@ "notes": "支持enable_thinking参数" }, - "qwen:qwen-turbo": { - "provider": "qwen", + "bailian:qwen-turbo": { + "provider": "bailian", "model_name": "qwen-turbo", "model_type": "chat", "series": "qwen-api", "display_name": "通义千问 Turbo", "context_window": 131072, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": true, "can_toggle_thinking": true, "supports_enable_thinking": true, @@ -207,15 +207,15 @@ "notes": "支持enable_thinking参数" }, - "qwen:qwen2.5-72b-instruct": { - "provider": "qwen", + "bailian:qwen2.5-72b-instruct": { + "provider": "bailian", "model_name": "qwen2.5-72b-instruct", "model_type": "chat", "series": "qwen2.5", "display_name": "Qwen 2.5 72B", "context_window": 131072, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": true, "can_toggle_thinking": true, "supports_enable_thinking": true, @@ -224,15 +224,15 @@ "notes": "支持enable_thinking参数" }, - "qwen:qwen2.5-32b-instruct": { - "provider": "qwen", + "bailian:qwen2.5-32b-instruct": { + "provider": "bailian", "model_name": "qwen2.5-32b-instruct", "model_type": "chat", "series": "qwen2.5", "display_name": "Qwen 2.5 32B", "context_window": 32768, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": true, "can_toggle_thinking": true, "supports_enable_thinking": true, @@ -241,59 +241,59 @@ "notes": "支持enable_thinking参数" }, - "qwen:qwen2.5-14b-instruct": { - "provider": "qwen", + "bailian:qwen2.5-14b-instruct": { + "provider": "bailian", "model_name": "qwen2.5-14b-instruct", "model_type": "chat", "series": "qwen2.5", "display_name": "Qwen 2.5 14B", "context_window": 32768, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": false }, "notes": "标准对话模型,不支持thinking" }, - "qwen:qwen2.5-7b-instruct": { - "provider": "qwen", + "bailian:qwen2.5-7b-instruct": { + "provider": "bailian", "model_name": "qwen2.5-7b-instruct", "model_type": "chat", "series": "qwen2.5", "display_name": "Qwen 2.5 7B", "context_window": 32768, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": false }, "notes": "标准对话模型,不支持thinking" }, - "_comment_qwen_deepseek": "=== 阿里百炼 DeepSeek系列 ===", + "_comment_bailian_deepseek": "=== 阿里百炼 DeepSeek系列 ===", - "qwen:deepseek-chat": { - "provider": "qwen", + "bailian:deepseek-chat": { + "provider": "bailian", "model_name": "deepseek-chat", "model_type": "chat", "series": "deepseek", "display_name": "DeepSeek Chat (百炼)", "context_window": 65536, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": false }, "notes": "标准对话模型" }, - "qwen:deepseek-r1": { - "provider": "qwen", + "bailian:deepseek-r1": { + "provider": "bailian", "model_name": "deepseek-r1", "model_type": "chat", "series": "deepseek-r1", "display_name": "DeepSeek R1 (百炼)", "context_window": 65536, "capabilities": { - "_inherit": "qwen.chat_capabilities", + "_inherit": "bailian.chat_capabilities", "supports_thinking": true, "can_toggle_thinking": false, "supports_reasoning_content": true @@ -301,71 +301,71 @@ "notes": "推理模型,内置思维链,不支持关闭" }, - "_comment_qwen_kimi": "=== 阿里百炼 Kimi系列 ===", + "_comment_bailian_kimi": "=== 阿里百炼 Kimi系列 ===", - "qwen:moonshot-v1-8k": { - "provider": "qwen", + "bailian:moonshot-v1-8k": { + "provider": "bailian", "model_name": "moonshot-v1-8k", "model_type": "chat", "series": "kimi", "display_name": "Kimi 8K (百炼)", "context_window": 8192, "capabilities": { - "_inherit": "qwen.chat_capabilities" + "_inherit": "bailian.chat_capabilities" }, "notes": "Kimi系列模型" }, - "qwen:moonshot-v1-32k": { - "provider": "qwen", + "bailian:moonshot-v1-32k": { + "provider": "bailian", "model_name": "moonshot-v1-32k", "model_type": "chat", "series": "kimi", "display_name": "Kimi 32K (百炼)", "context_window": 32768, "capabilities": { - "_inherit": "qwen.chat_capabilities" + "_inherit": "bailian.chat_capabilities" }, "notes": "Kimi系列模型" }, - "qwen:moonshot-v1-128k": { - "provider": "qwen", + "bailian:moonshot-v1-128k": { + "provider": "bailian", "model_name": "moonshot-v1-128k", "model_type": "chat", "series": "kimi", "display_name": "Kimi 128K (百炼)", "context_window": 131072, "capabilities": { - "_inherit": "qwen.chat_capabilities" + "_inherit": "bailian.chat_capabilities" }, "notes": "Kimi系列模型" }, - "_comment_qwen_glm": "=== 阿里百炼 GLM系列 ===", + "_comment_bailian_glm": "=== 阿里百炼 GLM系列 ===", - "qwen:glm-4-plus": { - "provider": "qwen", + "bailian:glm-4-plus": { + "provider": "bailian", "model_name": "glm-4-plus", "model_type": "chat", "series": "glm-4", "display_name": "GLM-4 Plus (百炼)", "context_window": 131072, "capabilities": { - "_inherit": "qwen.chat_capabilities" + "_inherit": "bailian.chat_capabilities" }, "notes": "GLM系列模型" }, - "_comment_qwen_embedding": "=== 阿里百炼 嵌入模型 ===", + "_comment_bailian_embedding": "=== 阿里百炼 嵌入模型 ===", - "qwen:text-embedding-v3": { - "provider": "qwen", + "bailian:text-embedding-v3": { + "provider": "bailian", "model_name": "text-embedding-v3", "model_type": "embedding", "display_name": "通用文本向量 v3", "capabilities": { - "_inherit": "qwen.embedding_capabilities", + "_inherit": "bailian.embedding_capabilities", "max_input_tokens": 8192, "default_dimensions": 1024, "supports_dimensions": true, @@ -374,26 +374,26 @@ "notes": "通用文本嵌入" }, - "qwen:text-embedding-v2": { - "provider": "qwen", + "bailian:text-embedding-v2": { + "provider": "bailian", "model_name": "text-embedding-v2", "model_type": "embedding", "display_name": "通用文本向量 v2", "capabilities": { - "_inherit": "qwen.embedding_capabilities", + "_inherit": "bailian.embedding_capabilities", "max_input_tokens": 2048, "default_dimensions": 1536 }, "notes": "通用文本嵌入" }, - "qwen:multimodal-embedding-one": { - "provider": "qwen", + "bailian:multimodal-embedding-one": { + "provider": "bailian", "model_name": "multimodal-embedding-one", "model_type": "embedding", "display_name": "多模态向量", "capabilities": { - "_inherit": "qwen.embedding_capabilities", + "_inherit": "bailian.embedding_capabilities", "max_input_tokens": 77, "default_dimensions": 1536, "supports_text_input": true, @@ -402,15 +402,15 @@ "notes": "支持文本和图像的多模态嵌入" }, - "_comment_qwen_rerank": "=== 阿里百炼 重排序模型 ===", + "_comment_bailian_rerank": "=== 阿里百炼 重排序模型 ===", - "qwen:gte-rerank": { - "provider": "qwen", + "bailian:gte-rerank": { + "provider": "bailian", "model_name": "gte-rerank", "model_type": "rerank", "display_name": "文本排序", "capabilities": { - "_inherit": "qwen.rerank_capabilities", + "_inherit": "bailian.rerank_capabilities", "max_documents": 50, "max_query_length": 2048 }, @@ -471,6 +471,144 @@ "default_dimensions": 1024 }, "notes": "文本嵌入模型" + }, + + "_comment_spark": "=== 讯飞星火 Spark ===", + + "spark:generalv3.5": { + "provider": "spark", + "model_name": "generalv3.5", + "model_type": "chat", + "series": "spark-v3", + "display_name": "星火 V3.5", + "context_window": 8192, + "capabilities": { + "_inherit": "spark.chat_capabilities" + }, + "notes": "星火通用模型 V3.5" + }, + + "spark:generalv3": { + "provider": "spark", + "model_name": "generalv3", + "model_type": "chat", + "series": "spark-v3", + "display_name": "星火 V3.0", + "context_window": 8192, + "capabilities": { + "_inherit": "spark.chat_capabilities" + }, + "notes": "星火通用模型 V3.0" + }, + + "spark:4.0Ultra": { + "provider": "spark", + "model_name": "4.0Ultra", + "model_type": "chat", + "series": "spark-v4", + "display_name": "星火 4.0 Ultra", + "context_window": 8192, + "capabilities": { + "_inherit": "spark.chat_capabilities" + }, + "notes": "星火 4.0 Ultra 旗舰模型" + }, + + "_comment_spark_embedding": "=== 讯飞星火 嵌入模型 ===", + + "spark:embedding-2.5": { + "provider": "spark", + "model_name": "embedding-2.5", + "model_type": "embedding", + "display_name": "星火文本嵌入 2.5", + "capabilities": { + "_inherit": "spark.embedding_capabilities", + "max_input_tokens": 8192, + "default_dimensions": 2560 + }, + "notes": "星火文本嵌入模型" + }, + + "_comment_wenxin": "=== 百度文心 Wenxin ===", + + "wenxin:ernie-4.0-turbo-8k": { + "provider": "wenxin", + "model_name": "ernie-4.0-turbo-8k", + "model_type": "chat", + "series": "ernie-4", + "display_name": "文心一言 4.0 Turbo", + "context_window": 8192, + "capabilities": { + "_inherit": "wenxin.chat_capabilities" + }, + "notes": "文心 4.0 Turbo 高性能模型" + }, + + "wenxin:ernie-4.0-8k": { + "provider": "wenxin", + "model_name": "ernie-4.0-8k", + "model_type": "chat", + "series": "ernie-4", + "display_name": "文心一言 4.0", + "context_window": 8192, + "capabilities": { + "_inherit": "wenxin.chat_capabilities" + }, + "notes": "文心 4.0 标准模型" + }, + + "wenxin:ernie-3.5-128k": { + "provider": "wenxin", + "model_name": "ernie-3.5-128k", + "model_type": "chat", + "series": "ernie-3", + "display_name": "文心一言 3.5 128K", + "context_window": 131072, + "capabilities": { + "_inherit": "wenxin.chat_capabilities" + }, + "notes": "文心 3.5 长上下文模型" + }, + + "wenxin:ernie-speed-128k": { + "provider": "wenxin", + "model_name": "ernie-speed-128k", + "model_type": "chat", + "series": "ernie-speed", + "display_name": "文心一言 Speed", + "context_window": 131072, + "capabilities": { + "_inherit": "wenxin.chat_capabilities" + }, + "notes": "文心 Speed 快速响应模型" + }, + + "wenxin:ernie-lite-8k": { + "provider": "wenxin", + "model_name": "ernie-lite-8k", + "model_type": "chat", + "series": "ernie-lite", + "display_name": "文心一言 Lite", + "context_window": 8192, + "capabilities": { + "_inherit": "wenxin.chat_capabilities" + }, + "notes": "文心 Lite 轻量级模型" + }, + + "_comment_wenxin_embedding": "=== 百度文心 嵌入模型 ===", + + "wenxin:embedding-v1": { + "provider": "wenxin", + "model_name": "embedding-v1", + "model_type": "embedding", + "display_name": "文心向量 V1", + "capabilities": { + "_inherit": "wenxin.embedding_capabilities", + "max_input_tokens": 384, + "default_dimensions": 384 + }, + "notes": "文心文本嵌入模型" } } } diff --git a/deploy/chart/euler_copilot/configs/framework/provider_config_summary.md b/deploy/chart/euler_copilot/configs/framework/provider_config_summary.md new file mode 100644 index 0000000000000000000000000000000000000000..fabc7edb0416ee8eab537f62d855c87e47985165 --- /dev/null +++ b/deploy/chart/euler_copilot/configs/framework/provider_config_summary.md @@ -0,0 +1,146 @@ +# Provider配置更新总结 + +## 更新时间 +2025年11月10日 + +## 更新内容 + +### 1. 新增在线服务平台配置(已完成) + +#### Spark(讯飞星火) +- **API地址**: `https://spark-api-open.xf-yun.com/v1` +- **认证方式**: Bearer Token +- **能力特点**: + - ✅ 支持流式输出、函数调用、JSON模式 + - ✅ 支持 top_k 参数 + - ✅ 不支持 thinking/reasoning +- **配置模型**: + - `spark:generalv3.5` - 星火 V3.5 + - `spark:generalv3` - 星火 V3.0 + - `spark:4.0Ultra` - 星火 4.0 Ultra + - `spark:embedding-2.5` - 星火文本嵌入 2.5 + +#### Wenxin(百度文心) +- **API地址**: `https://qianfan.baidubce.com/v2` +- **认证方式**: Bearer Token +- **能力特点**: + - ✅ 支持流式输出、函数调用、JSON模式 + - ✅ 支持 frequency_penalty 和 presence_penalty + - ✅ 支持联网搜索(enable_search) + - ✅ 不支持 top_k +- **配置模型**: + - `wenxin:ernie-4.0-turbo-8k` - 文心一言 4.0 Turbo + - `wenxin:ernie-4.0-8k` - 文心一言 4.0 + - `wenxin:ernie-3.5-128k` - 文心一言 3.5 128K + - `wenxin:ernie-speed-128k` - 文心一言 Speed + - `wenxin:ernie-lite-8k` - 文心一言 Lite + - `wenxin:embedding-v1` - 文心向量 V1 + +### 2. 新增本地部署框架配置(已完成) + +#### Ollama +- **默认API地址**: `http://localhost:11434/v1` +- **认证方式**: 无需认证(none) +- **能力特点**: + - ✅ 完整的OpenAI兼容API + - ✅ 支持流式、函数调用、JSON模式 + - ✅ 支持 top_k、frequency_penalty、presence_penalty + - ⚠️ API地址可由用户自定义 +- **参考文档**: https://ollama.com/docs/api + +#### vLLM +- **默认API地址**: `http://localhost:8000/v1` +- **认证方式**: 无需认证(none) +- **能力特点**: + - ✅ 高性能OpenAI兼容API + - ✅ 支持流式、函数调用、JSON模式 + - ✅ 支持 top_k、frequency_penalty、presence_penalty + - ✅ 支持 extra_body 扩展参数 + - ⚠️ API地址可由用户自定义 +- **参考文档**: https://vllm.readthedocs.io/ + +#### MindIE(华为昇腾) +- **默认API地址**: `http://localhost:8080/v1` +- **认证方式**: 无需认证(none) +- **能力特点**: + - ✅ 华为昇腾硬件优化 + - ✅ OpenAI兼容API + - ✅ 支持流式、函数调用、JSON模式 + - ✅ 支持 top_k、extra_body + - ⚠️ API地址可由用户自定义 +- **参考文档**: https://www.hiascend.com/document/detail/zh/canncommercial/60RC1alpha002/developer/mindie/mindie_01_0001.html + +#### ModelScope(魔塔) +- **默认API地址**: `http://localhost:8000/v1` +- **认证方式**: 无需认证(none) +- **能力特点**: + - ✅ 阿里巴巴模型平台 + - ✅ OpenAI兼容API + - ✅ 支持流式、函数调用、JSON模式 + - ✅ 支持 top_k、frequency_penalty、extra_body + - ⚠️ API地址可由用户自定义 +- **参考文档**: https://modelscope.cn/docs + +## 配置文件位置 + +- **供应商配置**: `/opt/euler-copilot-framework/deploy/chart/euler_copilot/configs/framework/providers.conf` +- **模型配置**: `/opt/euler-copilot-framework/deploy/chart/euler_copilot/configs/framework/models.conf` + +## 完整的Provider清单 + +### 在线服务(Public) +1. ✅ OpenAI +2. ✅ SiliconFlow(硅基流动) +3. ✅ Bailian(阿里百炼) +4. ✅ Baichuan(百川智能) +5. ✅ **Spark(讯飞星火)** - 新增 +6. ✅ **Wenxin(百度文心)** - 新增 + +### 本地部署(Private) +1. ✅ **Ollama** - 新增完整配置 +2. ✅ **vLLM** - 新增完整配置 +3. ✅ **MindIE** - 新增完整配置 +4. ✅ **ModelScope** - 新增完整配置 + +## 技术说明 + +### 认证类型说明 +- **bearer**: Bearer Token认证,需要在Authorization头中提供token +- **none**: 无需认证,适用于本地部署的服务 + +### API地址说明 +- 在线服务:API地址固定,直接使用配置文件中的地址 +- 本地部署:API地址为默认值,用户需要根据实际部署情况修改 + +### 能力继承机制 +- 模型配置通过 `_inherit` 字段继承供应商的默认能力 +- 模型可以覆盖供应商的部分能力配置 +- 例如:`"_inherit": "spark.chat_capabilities"` 继承spark的chat能力 + +## 使用建议 + +### 本地部署框架使用步骤 +1. 部署本地推理服务(如Ollama、vLLM等) +2. 在配置中修改对应provider的`api_base_url`为实际地址 +3. 在前端配置LLM时,选择对应的provider +4. 填写模型名称(需与本地部署的模型名称一致) + +### 配置验证 +```bash +# 验证providers.conf格式 +python3 -m json.tool deploy/chart/euler_copilot/configs/framework/providers.conf + +# 验证models.conf格式 +python3 -m json.tool deploy/chart/euler_copilot/configs/framework/models.conf +``` + +## 已验证状态 +- ✅ providers.conf JSON格式正确 +- ✅ models.conf JSON格式正确 +- ✅ 所有provider已在llm_provider_dict中注册 +- ✅ 所有provider已在AdapterFactoryV2中注册 + +## 下一步建议 +1. 添加具体的本地模型配置到models.conf(如ollama支持的具体模型) +2. 完善本地部署框架的文档和使用示例 +3. 测试各provider的实际调用情况 diff --git a/deploy/chart/euler_copilot/configs/framework/providers.conf b/deploy/chart/euler_copilot/configs/framework/providers.conf index 987f7c9296e9ac79168e65f1189991f11d311288..8554cfc2661dcc48c2a81f33a52150d2968fce59 100644 --- a/deploy/chart/euler_copilot/configs/framework/providers.conf +++ b/deploy/chart/euler_copilot/configs/framework/providers.conf @@ -2,6 +2,47 @@ "_comment": "供应商能力配置 - 根据API手册定义各供应商的默认能力", "version": "2.0", "providers": { + "openai": { + "provider_name": "openai", + "api_base_url": "https://api.openai.com/v1", + "auth_type": "bearer", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": true, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": false, + "supports_frequency_penalty": true, + "supports_presence_penalty": true, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": false, + "supports_stream_options": true, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 8192, + "supports_batch": true, + "default_dimensions": 1536, + "supports_encoding_format": true, + "supports_dimensions": true, + "available_dimensions": [256, 512, 1024, 1536, 3072], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "OpenAI API - 基于官方文档 https://platform.openai.com/docs/api-reference 校对 (2024)" + }, "siliconflow": { "provider_name": "siliconflow", "api_base_url": "https://api.siliconflow.cn/v1", @@ -49,8 +90,8 @@ }, "notes": "硅基流动API - 根据官方文档 https://docs.siliconflow.cn/cn/api-reference/" }, - "qwen": { - "provider_name": "qwen", + "bailian": { + "provider_name": "bailian", "api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "auth_type": "bearer", "auth_header": "Authorization", @@ -136,6 +177,252 @@ "supports_image_input": false }, "notes": "百川智能API - 根据官方文档 https://platform.baichuan-ai.com/docs/api" + }, + "spark": { + "provider_name": "spark", + "api_base_url": "https://spark-api-open.xf-yun.com/v1", + "auth_type": "bearer", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": true, + "supports_frequency_penalty": false, + "supports_presence_penalty": false, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": false, + "supports_stream_options": false, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 8192, + "supports_batch": true, + "default_dimensions": 2560, + "supports_encoding_format": false, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "讯飞星火API - 支持OpenAI兼容接口 https://www.xfyun.cn/doc/spark/" + }, + "wenxin": { + "provider_name": "wenxin", + "api_base_url": "https://qianfan.baidubce.com/v2", + "auth_type": "bearer", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": false, + "supports_frequency_penalty": true, + "supports_presence_penalty": true, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": false, + "supports_stream_options": false, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": true + }, + "embedding_capabilities": { + "max_input_tokens": 384, + "supports_batch": true, + "default_dimensions": 384, + "supports_encoding_format": false, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "百度千帆大模型API - 支持OpenAI兼容接口 https://cloud.baidu.com/doc/WENXINWORKSHOP/" + }, + "ollama": { + "provider_name": "ollama", + "api_base_url": "http://localhost:11434/v1", + "auth_type": "none", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": true, + "supports_frequency_penalty": true, + "supports_presence_penalty": true, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": false, + "supports_stream_options": true, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 2048, + "supports_batch": true, + "default_dimensions": 768, + "supports_encoding_format": false, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "Ollama本地部署框架 - OpenAI兼容API https://ollama.com/docs/api" + }, + "vllm": { + "provider_name": "vllm", + "api_base_url": "http://localhost:8000/v1", + "auth_type": "none", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": true, + "supports_frequency_penalty": true, + "supports_presence_penalty": true, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": true, + "supports_stream_options": true, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 2048, + "supports_batch": true, + "default_dimensions": 768, + "supports_encoding_format": true, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "vLLM高性能推理引擎 - OpenAI兼容API https://vllm.readthedocs.io/" + }, + "mindie": { + "provider_name": "mindie", + "api_base_url": "http://localhost:8080/v1", + "auth_type": "none", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": true, + "supports_frequency_penalty": false, + "supports_presence_penalty": false, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": true, + "supports_stream_options": true, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 2048, + "supports_batch": true, + "default_dimensions": 768, + "supports_encoding_format": false, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "MindIE华为昇腾推理引擎 - OpenAI兼容API https://www.hiascend.com/document/detail/zh/canncommercial/60RC1alpha002/developer/mindie/mindie_01_0001.html" + }, + "modelscope": { + "provider_name": "modelscope", + "api_base_url": "http://localhost:8000/v1", + "auth_type": "none", + "auth_header": "Authorization", + "chat_capabilities": { + "supports_streaming": true, + "supports_function_calling": true, + "supports_json_mode": true, + "supports_structured_output": false, + "supports_thinking": false, + "can_toggle_thinking": false, + "supports_reasoning_content": false, + "max_tokens_param": "max_tokens", + "supports_temperature": true, + "supports_top_p": true, + "supports_top_k": true, + "supports_frequency_penalty": true, + "supports_presence_penalty": false, + "supports_min_p": false, + "supports_response_format": true, + "supports_tools": true, + "supports_tool_choice": true, + "supports_extra_body": true, + "supports_stream_options": true, + "supports_enable_thinking": false, + "supports_thinking_budget": false, + "supports_enable_search": false + }, + "embedding_capabilities": { + "max_input_tokens": 2048, + "supports_batch": true, + "default_dimensions": 768, + "supports_encoding_format": true, + "supports_dimensions": false, + "available_dimensions": [], + "supports_text_input": true, + "supports_image_input": false + }, + "notes": "ModelScope魔塔模型平台 - OpenAI兼容API https://modelscope.cn/docs" } } } diff --git a/deploy/chart/euler_copilot/templates/framework/framework.yaml b/deploy/chart/euler_copilot/templates/framework/framework.yaml index c418879df2c996b3a4efeab0bfbdbeb54729214b..3094a94a46b71cfcbd74bae538241668ccbbe05b 100644 --- a/deploy/chart/euler_copilot/templates/framework/framework.yaml +++ b/deploy/chart/euler_copilot/templates/framework/framework.yaml @@ -55,11 +55,16 @@ spec: value: "Asia/Shanghai" - name: CONFIG value: "/app/config/config.toml" + - name: PROVIDERS_CONFIG + value: "/app/config/providers.conf" - name: MODELS_CONFIG value: "/app/config/models.conf" volumeMounts: - mountPath: /app/config name: framework-shared + - mountPath: /app/config/providers.conf + name: framework-config + subPath: providers.conf - mountPath: /app/config/models.conf name: framework-config subPath: models.conf