From 7b37a5e8be2c73d89cd32728e095327e93adb8b3 Mon Sep 17 00:00:00 2001 From: cardosofede Date: Mon, 31 Aug 2026 17:25:33 +0300 Subject: [PATCH] feat(connectors): expose config field prompts in the config-map endpoint The Hummingbot config maps carry a human readable prompt per field, but /connectors/{connector}/config-map only returned the type and whether the field was required, so clients had to invent their own labels. Prompts live in json_schema_extra either as a plain string or as a callable taking the config map, so callables are resolved before being returned and a failing one just omits the key instead of breaking the endpoint. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HbBYBEcgRVGXiZiq7kLDBv --- routers/connectors.py | 12 ++++++----- services/unified_connector_service.py | 31 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/routers/connectors.py b/routers/connectors.py index fa897c9a..b31ea98c 100644 --- a/routers/connectors.py +++ b/routers/connectors.py @@ -36,19 +36,21 @@ async def get_connector_config_map(connector_name: str, accounts_service: Accoun Each field contains: - type: The expected data type (e.g., "str", "SecretStr", "int") - required: Whether the field is required + - allowed_values: List of accepted values (only for Literal fields) + - prompt: Human readable prompt from the Hummingbot config map (when defined) """ return accounts_service.get_connector_config_map(connector_name) @router.get("/{connector_name}/trading-rules") async def get_trading_rules( - request: Request, + request: Request, connector_name: str, trading_pairs: Optional[List[str]] = Query(default=None, description="Filter by specific trading pairs") ): """ Get trading rules for a connector, optionally filtered by trading pairs. - + This endpoint uses the MarketDataService to access non-trading connector instances, which means no authentication or account setup is required. @@ -68,12 +70,12 @@ async def get_trading_rules( # Get trading rules (filtered by trading pairs if provided) rules = await market_data_service.get_trading_rules(connector_name, trading_pairs) - + if "error" in rules: raise HTTPException(status_code=404, detail=f"Connector '{connector_name}' not found or error: {rules['error']}") - + return rules - + except HTTPException: raise except Exception as e: diff --git a/services/unified_connector_service.py b/services/unified_connector_service.py index 6fe4d7af..7d78a506 100644 --- a/services/unified_connector_service.py +++ b/services/unified_connector_service.py @@ -1361,10 +1361,41 @@ def get_connector_config_map(connector_name: str): field_info = {"type": type_name, "required": field.is_required()} if allowed_values is not None: field_info["allowed_values"] = allowed_values + + prompt = UnifiedConnectorService._resolve_field_prompt( + field, connector_config.hb_config + ) + if prompt is not None: + field_info["prompt"] = prompt + fields_info[key] = field_info return fields_info + @staticmethod + def _resolve_field_prompt(field, config_map) -> Optional[str]: + """Resolve the Hummingbot prompt of a config field, if it defines one. + + Prompts are stored in ``json_schema_extra`` either as a plain string or as + a callable taking the config map, so callables are evaluated here. + """ + extra = field.json_schema_extra + if not isinstance(extra, dict): + return None + + prompt = extra.get("prompt") + if prompt is None: + return None + + if callable(prompt): + try: + prompt = prompt(config_map) + except Exception as e: + logger.debug(f"Could not resolve prompt for config field: {e}") + return None + + return str(prompt) if prompt is not None else None + # ========================================================================= # Cleanup # =========================================================================