Skip to content

symfonic.platform.transport_setting_refusal

transport_setting_refusal

Map a composer's refused FrameworkConfig the way a refused config already maps.

CORE-235. An AgentHost composer that builds its Agent with symfonic.migration.agent_from_config refuses unsupported settings with UnsupportedFrameworkSettingError before any model, memory or tool call. In _agent_for that refusal used to escape as an unhandled ASGI exception.

It is mapped exactly as the native stream maps a RetiredConfigurationError before response start: a 502 whose body says only that the turn failed, since the exception text quotes configuration values. The operator log carries the versioned contract and the refused setting paths, never their values.

The class is looked up in sys.modules rather than imported, the idiom :func:~symfonic.platform.agent_boundary.require_supported_agent uses: the error can only exist if its module is loaded, and importing it here would load the legacy FrameworkConfig into every native router. Any other exception is not a refused setting.

refused_setting

refused_setting(error: BaseException) -> bool

Log a refused config for the operator and say whether error is one.

The caller maps True to its 502; this module stays free of FastAPI.

Source code in src/symfonic/platform/transport_setting_refusal.py
def refused_setting(error: BaseException) -> bool:
    """Log a refused config for the operator and say whether ``error`` is one.

    The caller maps ``True`` to its 502; this module stays free of FastAPI.
    """
    refused_type: Any = getattr(
        sys.modules.get(_CONTRACT_MODULE), "UnsupportedFrameworkSettingError", None
    )
    if not (isinstance(refused_type, type) and isinstance(error, refused_type)):
        return False
    logger.error(
        "agent composition refused before egress (%s, %s): %s",
        type(error).__name__,
        getattr(error, "contract", "?"),
        ", ".join(getattr(error, "settings", ())),
    )
    return True