Validate Configuration

View as Markdown

The example validator treats component configuration as one coherent contract. It requires strings and booleans where declared, accepts only observe or enforce for requests.mode, requires arrays of strings for block and redaction keys, verifies integer priorities, and reports every unknown component-local field under a stable code.

Validation does not open a client, register middleware, emit an event, or alter active configuration. Relay validates disabled components too, which lets an operator stage a future component and discover its mistakes before rollout.

Return the Same Diagnostic Shape

The following implementations return the same diagnostic fields while using each binding’s native configuration types:

1def validate_documentation_config(config: dict[str, Any]) -> list[dict[str, str]]:
2 diagnostics: list[dict[str, str]] = []
3 allowed_top_level = {"tag", *GROUP_FIELDS}
4 for key in config.keys() - allowed_top_level:
5 diagnostics.append(_diagnostic(
6 "warning", "unknown_field", key,
7 f"unknown field '{key}' is not supported",
8 ))
9
10 for group, allowed in GROUP_FIELDS.items():
11 value = config.get(group)
12 if value is not None and not isinstance(value, dict):
13 diagnostics.append(_diagnostic(
14 "error", "invalid_config", group,
15 f"{group} must be an object",
16 ))
17 continue
18 if isinstance(value, dict):
19 for key in value.keys() - allowed:
20 field = f"{group}.{key}"
21 diagnostics.append(_diagnostic(
22 "warning", "unknown_field", field,
23 f"unknown field '{field}' is not supported",
24 ))
25
26 settings = normalized_config(config)
27 expected_types = {
28 "tag": str,
29 "observe.enabled": bool,
30 "observe.redact_keys": list,
31 "requests.enabled": bool,
32 "requests.mode": str,
33 "requests.blocked_tools": list,
34 "requests.blocked_models": list,
35 "requests.header_name": str,
36 "requests.header_value": str,
37 "requests.priority": int,
38 "requests.break_chain": bool,
39 "execution.enabled": bool,
40 "execution.priority": int,
41 "execution.emit_pending_marks": bool,
42 "runtime.emit_marks": bool,
43 "runtime.emit_isolated_scope": bool,
44 }
45 for field, expected in expected_types.items():
46 group, separator, key = field.partition(".")
47 value = settings[group][key] if separator else settings[group]
48 if type(value) is not expected:
49 diagnostics.append(_diagnostic(
50 "error", "invalid_config", field,
51 f"{field} must be a {expected.__name__}",
52 ))
53 for field in ("observe.redact_keys", "requests.blocked_tools", "requests.blocked_models"):
54 group, key = field.split(".")
55 value = settings[group][key]
56 if isinstance(value, list) and not all(isinstance(item, str) for item in value):
57 diagnostics.append(_diagnostic(
58 "error", "invalid_config", field,
59 f"{field} must contain only strings",
60 ))
61 if isinstance(settings["tag"], str) and not settings["tag"]:
62 diagnostics.append(_diagnostic(
63 "error", "invalid_tag", "tag", "tag must be a non-empty string",
64 ))
65 for field in ("requests.header_name", "requests.header_value"):
66 group, key = field.split(".")
67 if isinstance(settings[group][key], str) and not settings[group][key]:
68 diagnostics.append(_diagnostic(
69 "error", "invalid_header", field, f"{field} must be a non-empty string",
70 ))
71 if settings["requests"]["mode"] not in {"observe", "enforce"}:
72 diagnostics.append(_diagnostic(
73 "error", "unsupported_mode", "requests.mode",
74 "requests.mode must be either observe or enforce",
75 ))
76 return diagnostics
77
78class DocumentationPlugin:
79 def validate(self, config: dict[str, Any]) -> list[dict[str, str]]:
80 return validate_documentation_config(config)

The checked implementations continue beyond these excerpts by checking every boolean, integer, string, and string-array field. The important ordering is visible here: report unknown paths, deserialize or merge defaults, then apply semantic rules to the normalized value. A wrong object shape therefore produces a type diagnostic instead of an exception that escapes validation.

Rust receives the host ConfigPolicy only through validate_with_policy. The default implementation calls validate and preserves existing custom-plugin behavior, so a Rust plugin that claims policy-controlled unknown-field diagnostics must override the policy method. Python and Node.js component hooks receive only component-local config; their example makes its unknown-field policy an explicit part of the implementation contract.

One unsupported value returns equivalent data in every binding:

1{
2 "level": "error",
3 "code": "documentation-plugin.unsupported_mode",
4 "component": "documentation-plugin",
5 "field": "requests.mode",
6 "message": "requests.mode must be either observe or enforce"
7}

Preflight the Effective Document

Use the following procedure to prove that validation remains separate from activation:

  1. Register documentation-plugin, then call the binding’s list function. Rust uses list_plugin_kinds, Python uses list_kinds, and Node.js uses listKinds. The kind should appear even though no component is active.
  2. Construct a component with enabled = false and requests.mode = "invalid". Call validate_plugin_config, plugin.validate, or plugin.validate. Confirm the report contains documentation-plugin.unsupported_mode and that the active report remains unchanged.
  3. Correct the mode, give requests.priority a string, and confirm the error identifies that field. Restore the integer, add an unknown key, and confirm that the Python and Node.js examples return their documented warning while the Rust example follows the supplied ConfigPolicy.
  4. Validate the shared correct configuration. The report should have no error-level diagnostics.
  5. Remember that initialize also layers discovered plugins.toml configuration. In a deployment that uses file discovery, validate the effective startup source rather than assuming an isolated in-memory report describes the final activation.

Success means every binding catches the same invalid values before registration, a disabled component remains visible to validation, diagnostics are stable enough for automation, and the valid configuration proceeds to registration without a second interpretation of its fields.