feat: native module config macro#2503
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #2503 +/- ##
==========================================
- Coverage 70.09% 69.97% -0.13%
==========================================
Files 838 838
Lines 74337 74337
Branches 6667 6667
==========================================
- Hits 52109 52018 -91
- Misses 20553 20618 +65
- Partials 1675 1701 +26
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Greptile SummaryThis PR introduces a
Confidence Score: 4/5Safe to merge after addressing the missing skip_serializing guard in check_field_serde; all migrated callers use the macro correctly and the runtime key-set check is sound. The macro's compile-time rejection list covers the main bypass cases (Option, serde(default), skip, skip_deserializing, flatten), but #[serde(skip_serializing)] and #[serde(skip_serializing_if)] are omitted. A field annotated with either will pass the compile-time check yet trigger an always-reject in enforce_one_to_one at runtime — the process can never start. The fix is a one-line addition to check_field_serde matching the existing skip/skip_deserializing branch. native/rust/dimos-module-macros/src/lib.rs — check_field_serde needs to also forbid skip_serializing and skip_serializing_if. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["#[native_config] applied to struct"] --> B{check_native_config}
B -->|tuple struct / enum| C["compile error: wrong type"]
B -->|Option field| D["compile error: Option forbidden"]
B -->|serde default/skip/flatten| E["compile error: attribute forbidden"]
B -->|passes| F["inject derive Debug+Deserialize+Serialize+Validate\n+ serde(deny_unknown_fields)\n+ impl NativeConfig"]
F --> G["Module::Config bound: ModuleConfig\n= DeserializeOwned+Serialize+Debug+Validate+NativeConfig"]
G --> H["run() reads stdin JSON"]
H --> I["parse_config_json: serde::from_value"]
I -->|deser error| J["io::Error: failed to deserialize"]
I -->|ok| K["enforce_one_to_one\ncompare provided_keys vs expected_keys"]
K -->|missing or unexpected keys| L["io::Error: config keys do not match\n(catches Option type-aliases)"]
K -->|keys match| M["validate_config: config.validate()"]
M -->|validation error| N["io::Error: config validation failed"]
M -->|ok| O["Module::build → run loop"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["#[native_config] applied to struct"] --> B{check_native_config}
B -->|tuple struct / enum| C["compile error: wrong type"]
B -->|Option field| D["compile error: Option forbidden"]
B -->|serde default/skip/flatten| E["compile error: attribute forbidden"]
B -->|passes| F["inject derive Debug+Deserialize+Serialize+Validate\n+ serde(deny_unknown_fields)\n+ impl NativeConfig"]
F --> G["Module::Config bound: ModuleConfig\n= DeserializeOwned+Serialize+Debug+Validate+NativeConfig"]
G --> H["run() reads stdin JSON"]
H --> I["parse_config_json: serde::from_value"]
I -->|deser error| J["io::Error: failed to deserialize"]
I -->|ok| K["enforce_one_to_one\ncompare provided_keys vs expected_keys"]
K -->|missing or unexpected keys| L["io::Error: config keys do not match\n(catches Option type-aliases)"]
K -->|keys match| M["validate_config: config.validate()"]
M -->|validation error| N["io::Error: config validation failed"]
M -->|ok| O["Module::build → run loop"]
Reviews (3): Last reviewed commit: "Check" | Re-trigger Greptile |
Problem
Config defaults for native modules should only be declared in one place (the Python wrapper) and the native module config should have fields iff they are supplied by Python.
Closes DIM-XXX
Solution
Introduce a native_config proc macro for Rust native module config structs. It reduces the boiler plate and ensure that there are no optional configs or default values.
How to Test
Contributor License Agreement