"""外部系统 provider 注册表。 标准 OpenAPI 与 Streamable HTTP MCP 系统均通过数据库配置接入。 """ from __future__ import annotations from dataclasses import dataclass from typing import Any from .auth import ExternalAuthError, get_auth_strategy @dataclass(frozen=True) class ProviderSpec: provider: str title: str connector: str default_auth_type: str allowed_auth_types: tuple[str, ...] defaults: dict[str, Any] _PROVIDERS = { "generic_openapi": ProviderSpec( provider="generic_openapi", title="通用 OpenAPI 系统", connector="openapi", default_auth_type="password_jwt", allowed_auth_types=("password_jwt", "api_key", "bearer_token"), defaults={ "login_path": "/api/auth/token/", "username_field": "username", "password_field": "password", "token_field": "access", "auth_header_name": "Authorization", "auth_header_template": "Bearer {token}", "query_guidance": "", "recommended_operation_ids": [], "operation_mode": "query", "operation_policies": {}, }, ), "generic_mcp": ProviderSpec( provider="generic_mcp", title="通用 MCP 系统", connector="mcp", default_auth_type="password_jwt", allowed_auth_types=("password_jwt", "api_key", "bearer_token"), defaults={ "login_path": "/api/auth/token/", "username_field": "username", "password_field": "password", "token_field": "access", "auth_header_name": "Authorization", "auth_header_template": "Bearer {token}", "query_guidance": "", "recommended_operation_ids": [], "operation_mode": "upstream_managed", "operation_policies": {}, }, ), } def get_provider(provider: str) -> ProviderSpec: result = _PROVIDERS.get((provider or "").strip()) if result is None: raise ValueError(f"不支持的外部系统 provider: {provider}") return result def provider_specs() -> tuple[ProviderSpec, ...]: return tuple(_PROVIDERS.values()) def merged_config(provider: str, config: dict[str, Any]) -> dict[str, Any]: spec = get_provider(provider) result = {**spec.defaults, **(config or {})} auth_type = str(result.get("auth_type") or spec.default_auth_type).strip() if auth_type not in spec.allowed_auth_types: raise ValueError(f"{spec.title} 不支持认证方式 {auth_type}") result["auth_type"] = auth_type return result def credential_fields(provider: str, config: dict[str, Any]) -> list[dict[str, Any]]: merged = merged_config(provider, config) try: strategy = get_auth_strategy(merged["auth_type"]) except ExternalAuthError as exc: raise ValueError(str(exc)) from exc return [field.as_dict() for field in strategy.credential_fields]