Summary
The gateway/bot config schema validates almost everything a user writes in gateway.yaml — channels, sessions, streaming, delivery, STT, daemon — with friendly, field-by-field errors. But the one block that controls the server itself (gateway:) is modelled as an opaque Optional[Dict[str, Any]]. Every server knob an operator actually tunes for a robust deployment — drain_timeout, reload_drain_timeout, admission ceiling, health-monitor thresholds, host/port, and the inbound hooks — is read ad-hoc deep inside server.py and is never validated. A typo (drain_timout), a wrong type ("10s" instead of 10), or an out-of-place key is accepted at load time and then silently ignored at runtime, so the gateway quietly runs with defaults the operator believes they overrode. This is exactly the class of "it validated fine but didn't do what I wrote" failure that erodes trust in a production gateway and makes the settings undiscoverable (no schema = no autocomplete, no docs surface, no error).
Current behaviour
The canonical schema explicitly keeps the server block permissive and unvalidated:
src/praisonai-bot/praisonai_bot/bots/_config_schema.py
# Gateway server settings (host/port, drain_timeout, admission control,
# etc.) and inbound trigger hooks. These are read by
# ``gateway/server.py::load_gateway_config`` / ``_apply_hooks_from_config``
# rather than modelled field-by-field here; kept permissive so a real
# ``gateway.yaml`` with a top-level ``gateway:``/``hooks:`` block validates
# through this single schema instead of being rejected.
gateway: Optional[Dict[str, Any]] = None
hooks: Optional[List[Dict[str, Any]]] = None
Those settings are then pulled out by string key at runtime, with no schema behind them, so a misspelled or mistyped key simply falls through to the default:
src/praisonai-bot/praisonai_bot/gateway/server.py
reload_drain_cfg = gw_cfg.get("reload_drain_timeout") # typo => None => default, silently
...
# drain_timeout, admission ceiling, health-monitor thresholds, hooks:
# all read via gw_cfg.get(...) with no validation of names, types, or ranges
By contrast, core already ships a typed dataclass tree for exactly these settings in src/praisonai-agents/praisonaiagents/gateway/config.py (GatewayConfig with host/port/max_connections/drain/liveness/admission fields, LivenessConfig, etc.), but the wrapper's load-time schema does not use or mirror it for the gateway: block — so the strong typing that exists in core never reaches the point where user YAML is validated.
Net effect: rich, friendly validation everywhere except the settings that determine the gateway's operational robustness.
Desired behaviour
The gateway: server block is validated field-by-field at load time, exactly like channels:/session:/streaming: already are:
- Unknown/misspelled keys produce a clear error (or an explicit warning) that names the offending key, instead of being silently dropped.
- Wrong types and out-of-range values (e.g. a negative
drain_timeout, a string where a number is expected) are rejected with a friendly message.
- The accepted settings become discoverable — one schema is the single source of truth for what the gateway server accepts, feeding docs and editor autocompletion.
- The typed shape reuses/mirrors core's existing
GatewayConfig so there is one definition of a gateway server setting, not two.
Layer placement
- Primary layer: wrapper
- Why not core: core already defines the typed
GatewayConfig dataclass; the gap is that load-time validation of user gateway.yaml — a wrapper responsibility (load_gateway_config / GatewayConfigSchema) — does not apply it. The missing work is in the wrapper's schema, not core's types.
- Why not tools: this is operator configuration of the gateway server, not an agent-callable integration.
- Why not plugins: it is validation of first-party gateway config, not a cross-cutting lifecycle hook; plugin-declared channel fields already have their own descriptor path and are unaffected.
- Secondary touch: core (
praisonaiagents/gateway/config.py) — ensure GatewayConfig/LivenessConfig cover every server knob server.py reads (reload_drain_timeout, health-monitor thresholds, hooks) so the wrapper schema can derive from a complete core definition rather than re-declaring fields.
- 3-way surface (CLI + YAML + Python): primarily YAML (validated at load), with the same typed shape available to Python users constructing config programmatically; no new CLI verb required.
Proposed approach
- Extension point: replace
gateway: Optional[Dict[str, Any]] in GatewayConfigSchema with a typed GatewayServerSchema (a Pydantic model mirroring core's GatewayConfig), and validate hooks as a typed HookSchema list. Use model_config = ConfigDict(extra="forbid") (or an explicit warn-on-unknown pass) so misspelled keys are caught, keeping the friendly-error style already used across the schema.
Minimal API sketch:
class GatewayServerSchema(BaseModel):
model_config = ConfigDict(extra="forbid") # unknown keys -> clear error
host: str = "127.0.0.1"
port: int = Field(8000, ge=1, le=65535)
drain_timeout: float = Field(10.0, ge=0)
reload_drain_timeout: Optional[float] = Field(None, ge=0)
max_concurrent_runs: Optional[int] = Field(None, ge=1)
health: Optional[HealthMonitorSchema] = None
# ... derived from praisonaiagents.gateway.config.GatewayConfig
class GatewayConfigSchema(BaseModel):
gateway: Optional[GatewayServerSchema] = None
hooks: Optional[List[HookSchema]] = None
Resolution sketch
# gateway.yaml with a typo in a server knob
gateway:
drain_timout: 30 # <-- misspelled ("timout")
reload_drain_timeout: "quick" # <-- wrong type
# Before (today): validates fine, both settings silently ignored,
# gateway drains with the default 10s and the operator never knows.
GatewayConfigSchema(**yaml_load("gateway.yaml")) # OK — no error
# After (proposed): rejected at load with a precise message
# ValidationError: gateway.drain_timout: unexpected key (did you mean 'drain_timeout'?)
# gateway.reload_drain_timeout: value is not a valid float
Severity
High — silent config drop-through on the settings that govern drain, admission, and health is a genuine production-robustness and ease-of-use hazard, and the surrounding schema already sets the bar (every other block is validated). The fix reuses an existing core dataclass and follows an established pattern in the same file.
Validation
src/praisonai-bot/praisonai_bot/bots/_config_schema.py — gateway: Optional[Dict[str, Any]] with an inline comment stating it is "kept permissive" and the settings are "read ... rather than modelled field-by-field here."
src/praisonai-bot/praisonai_bot/gateway/server.py — server knobs (reload_drain_timeout, drain_timeout, admission ceiling, health thresholds, hooks) resolved via gw_cfg.get(...) with no name/type/range validation.
src/praisonai-agents/praisonaiagents/gateway/config.py — GatewayConfig/LivenessConfig already model these settings with types, confirming a single typed source could back the wrapper schema; the wrapper simply does not use it for the gateway: block.
Summary
The gateway/bot config schema validates almost everything a user writes in
gateway.yaml— channels, sessions, streaming, delivery, STT, daemon — with friendly, field-by-field errors. But the one block that controls the server itself (gateway:) is modelled as an opaqueOptional[Dict[str, Any]]. Every server knob an operator actually tunes for a robust deployment —drain_timeout,reload_drain_timeout, admission ceiling, health-monitor thresholds, host/port, and the inboundhooks— is read ad-hoc deep insideserver.pyand is never validated. A typo (drain_timout), a wrong type ("10s"instead of10), or an out-of-place key is accepted at load time and then silently ignored at runtime, so the gateway quietly runs with defaults the operator believes they overrode. This is exactly the class of "it validated fine but didn't do what I wrote" failure that erodes trust in a production gateway and makes the settings undiscoverable (no schema = no autocomplete, no docs surface, no error).Current behaviour
The canonical schema explicitly keeps the server block permissive and unvalidated:
src/praisonai-bot/praisonai_bot/bots/_config_schema.pyThose settings are then pulled out by string key at runtime, with no schema behind them, so a misspelled or mistyped key simply falls through to the default:
src/praisonai-bot/praisonai_bot/gateway/server.pyBy contrast, core already ships a typed dataclass tree for exactly these settings in
src/praisonai-agents/praisonaiagents/gateway/config.py(GatewayConfigwith host/port/max_connections/drain/liveness/admission fields,LivenessConfig, etc.), but the wrapper's load-time schema does not use or mirror it for thegateway:block — so the strong typing that exists in core never reaches the point where user YAML is validated.Net effect: rich, friendly validation everywhere except the settings that determine the gateway's operational robustness.
Desired behaviour
The
gateway:server block is validated field-by-field at load time, exactly likechannels:/session:/streaming:already are:drain_timeout, a string where a number is expected) are rejected with a friendly message.GatewayConfigso there is one definition of a gateway server setting, not two.Layer placement
GatewayConfigdataclass; the gap is that load-time validation of usergateway.yaml— a wrapper responsibility (load_gateway_config/GatewayConfigSchema) — does not apply it. The missing work is in the wrapper's schema, not core's types.praisonaiagents/gateway/config.py) — ensureGatewayConfig/LivenessConfigcover every server knobserver.pyreads (reload_drain_timeout, health-monitor thresholds,hooks) so the wrapper schema can derive from a complete core definition rather than re-declaring fields.Proposed approach
gateway: Optional[Dict[str, Any]]inGatewayConfigSchemawith a typedGatewayServerSchema(a Pydantic model mirroring core'sGatewayConfig), and validatehooksas a typedHookSchemalist. Usemodel_config = ConfigDict(extra="forbid")(or an explicit warn-on-unknown pass) so misspelled keys are caught, keeping the friendly-error style already used across the schema.Minimal API sketch:
Resolution sketch
Severity
High — silent config drop-through on the settings that govern drain, admission, and health is a genuine production-robustness and ease-of-use hazard, and the surrounding schema already sets the bar (every other block is validated). The fix reuses an existing core dataclass and follows an established pattern in the same file.
Validation
src/praisonai-bot/praisonai_bot/bots/_config_schema.py—gateway: Optional[Dict[str, Any]]with an inline comment stating it is "kept permissive" and the settings are "read ... rather than modelled field-by-field here."src/praisonai-bot/praisonai_bot/gateway/server.py— server knobs (reload_drain_timeout,drain_timeout, admission ceiling, health thresholds,hooks) resolved viagw_cfg.get(...)with no name/type/range validation.src/praisonai-agents/praisonaiagents/gateway/config.py—GatewayConfig/LivenessConfigalready model these settings with types, confirming a single typed source could back the wrapper schema; the wrapper simply does not use it for thegateway:block.