Configuring machine learning experiments with Fiddle
2025-03-16 — 2025-06-10
Wherein machine learning experiment configuration is recast as Python functions that produce Fiddle Buildables, and a command-line interface is emitted via Abseil using fdl_flags.DEFINE_fiddle_config
Fiddle is, AFAICT, a successor to gin for configuring ML experiments. It uses a code-based config system: we define Python functions that change the configuration. It’s a slightly different approach to gin. I’m not sure how to use it for things like hyperparameter search, but it looks… feasible?
It generates a CLI via Google’s absl system. The keyword is Flag Support.
My experience trying to use the code was ultimately negative. Fiddle’s codebase is enormous and incomprehensible, spanning many megabytes of Python code. Maybe this means it exposes the “right” API, but I don’t think it’s worth the confusion. Reading the code to understand what’s going on is beyond human capabilities, and in fact beyond vibe coding capabilities. [TODO clarify] Ultimately, even with an AI coding assistant, I spent the majority of development time fighting Fiddle rather than building my project. Resolving names is confusing to me, to the AI, and to the interactive stack debugger.
1 Fiddle User Guide: Configuration, CLI, and Experiment Best Practices
The docs are sparse; see: See the Fiddle documentation, especially
It’s currently in the middle of a major API update, and the HEAD version and the last release are very far apart.
I got Google Gemini to update the docs from the repository; that is what follows.
This guide provides practical advice and recommended patterns for using Fiddle, with a focus on command-line integration via fdl_flags and managing experimental workflows. It aims to clarify common points of confusion and offer robust solutions.
1.1 Core Philosophy: Configuration as Code, Separated Concerns
Business Logic is Fiddle-Agnostic: Write your core algorithms (models, data processing, physics simulators, training loops) in standard Python modules without any direct dependency on Fiddle. These components should accept parameters via their
__init__methods or function arguments.Example:
my_project/src/my_project/business_logic/model_architectures.pyConfiguration Logic in Dedicated Modules: Define your Fiddle configurations (
fdl.Config,fdl.Partial), base setups, and reusable transformations (fiddlers) in separate Python modules, typically within aconfigs/directory at the top level of your project or inside your main package.Example:
my_project/configs/base_model_configs.py,my_project/configs/fiddlers.pyLauncher Scripts for Execution: Use scripts in a
scripts/directory as entry points for running experiments. These scripts will usefdl_flagsto parse command-line arguments, assemble the final Fiddle configuration, and then, often via a job submission system likesubmitit, execute a worker function that uses the built Fiddle configuration.
1.2 Defining Configurations
1.2.1 Using @fdl.experimental.auto_config (Often Recommended for Simplicity)
Decorate Python functions that directly construct your object graph. Fiddle automatically translates these into functions that return fdl.Buildable objects when their .as_buildable() method is called.
# In configs/my_model_configs.py
import fiddle as fdl
from fiddle.experimental import auto_config
from src.my_project.business_logic import layers # Assuming layers.py exists
@auto_config.auto_config
def my_encoder_config(num_layers: int = 4, hidden_size: int = 256) -> layers.Encoder:
sub_layer_cfgs = []
for _ in range(num_layers):
sub_layer_cfgs.append(layers.AttentionLayer(num_heads=8, head_dim=hidden_size // 8))
return layers.Encoder(layers=sub_layer_cfgs, embedding_dim=hidden_size)
# To get the Fiddle config:
# cfg = my_encoder_config.as_buildable(num_layers=2)1.2.2 Manual fdl.Config / fdl.Partial Construction (Explicit Control)
We manually create fdl.Config or fdl.Partial instances and set their arguments.
# In configs/my_model_configs.py
import fiddle as fdl
import fiddle.printing #must be explicitly imported to use fdl.printing.as_str_flattened
from src.my_project.business_logic import layers
def my_encoder_config_manual(num_layers: int = 4, hidden_size: int = 256) -> fdl.Config[layers.Encoder]:
cfg = fdl.Config(layers.Encoder)
cfg.embedding_dim = hidden_size
sub_layer_cfgs = []
for _ in range(num_layers):
attn_cfg = fdl.Config(layers.AttentionLayer)
attn_cfg.num_heads = 8
attn_cfg.head_dim = hidden_size // 8
sub_layer_cfgs.append(attn_cfg)
cfg.layers = sub_layer_cfgs
return cfg
# To get the Fiddle config:
# cfg = my_encoder_config_manual(num_layers=2)1.3 Command-Line Interface with fdl_flags (New API)
IMPORTANT: We should always use the new Fiddle flags API (fdl_flags.DEFINE_fiddle_config and fdl_flags.DEFINE_fiddle_sweep) to let our scripts consume Fiddle configurations from the command line. Avoid the legacy --fdl.* flags.
1.3.1 Defining a Configuration Flag (in scripts/your_launcher.py)
from absl import app
import fiddle as fdl
from fiddle import absl_flags as fdl_flags
# Import modules that contain the functions/classes your CLI will reference
from configs import my_model_configs
from configs import fiddlers
# Define a flag. 'exp_cfg' becomes '--exp_cfg' on the CLI.
MY_EXPERIMENT_CONFIG = fdl_flags.DEFINE_fiddle_config(
name="exp_cfg", # The command-line flag name
default_module=[my_model_configs, fiddlers], # List of modules to search for names
default="config:my_model_configs.my_encoder_config_manual", # Default base config
help_string="Main configuration for the experiment.",
required=False # Because a default is provided
)
def main(argv):
# Accessing the fully resolved fdl.Buildable:
unbuilt_cfg: fdl.Buildable = MY_EXPERIMENT_CONFIG.value
# If --exp_cfg was NOT provided on CLI, and default is a simple 'config:name' string:
# You *might* find that unbuilt_cfg is a list like ['config:name'] instead of
# the fdl.Buildable. This seems to be a nuance in how FiddleFlag.value
# handles defaults when no other CLI processing for that flag occurs.
#
# ROBUST HANDLING (Workaround for potential FiddleFlag default behavior):
if isinstance(unbuilt_cfg, list) and all(isinstance(x, str) for x in unbuilt_cfg):
print("WARNING: Flag default resolved to command strings. Re-parsing...")
# Create a temporary parser with the same context as the original flag
# to correctly process the default command string(s).
# The .default_module attribute IS available on the FlagHolder.
temp_parser = fdl_flags.FiddleFlag(
name="_temp_default_parser", default=None,
parser=flags.ArgumentParser(), serializer=None, help_string="Internal",
default_module=MY_EXPERIMENT_CONFIG.default_module,
# allow_imports=MY_EXPERIMENT_CONFIG.allow_imports # If you explicitly set it
)
temp_parser.parse(unbuilt_cfg) # unbuilt_cfg here is the list of command strings
unbuilt_cfg = temp_parser.value
print(f"Re-parsed default. Type is now: {type(unbuilt_cfg)}")
if not isinstance(unbuilt_cfg, fdl.Buildable):
raise TypeError(f"Configuration did not resolve to an fdl.Buildable. Got: {unbuilt_cfg}")
# Now, unbuilt_cfg is guaranteed to be an fdl.Buildable
# print(fdl.printing.as_str_flattened(unbuilt_cfg))
# built_object = fdl.build(unbuilt_cfg)
# ... proceed with your experiment ...1.3.2 Invoking from the Command Line
Use the flag name defined in DEFINE_fiddle_config (for example, --exp_cfg) followed by Fiddle commands:
config:<name_or_path_to_fn>: Specifies the base configuration function or recipe.--exp_cfg=config:my_model_configs.my_encoder_config_manual--exp_cfg=config:'my_model_configs.my_encoder_config(num_layers=2)'(with arguments)
fiddler:<name_or_path_to_fn>: Applies a fiddler function. It can be repeated; fiddlers are applied in order.--exp_cfg=fiddler:fiddlers.apply_large_model_tweaks--exp_cfg=fiddler:'fiddlers.set_learning_rate(rate=0.005)'
set:<path.to.attribute>=<value>: Directly overrides a parameter. These overrides are applied after allconfig:andfiddler:commands from the CLI.--exp_cfg=set:model.layers[0].num_heads=12--exp_cfg=set:data_config.path='"gs://my-bucket/dataset.tfrecord"'(Note the nested quotes for string values).--exp_cfg=set:model.use_dropout=True(Booleans and numbers usually don’t need extra quotes).
config_file:<path_to_json>: Loads a base config from a Fiddle JSON file.config_str:<serialized_json_string>: Loads a base config from a Fiddle JSON string (often used by sweep launchers).
Example:
IMPORTANT: The default_module argument to DEFINE_fiddle_config is crucial. It’s a list of Python module objects where Fiddle will search for names like my_encoder_config or fiddlers.use_bfloat16_activations when they are not fully qualified on the command line. If a name is fully qualified (e.g., my_project.configs.my_model_configs.my_fn), default_module is less critical for that specific name resolution.
1.3.3 Managing Multiple Experiment Types / CLI Entry Points
If we have distinct experimental setups (e.g., one using SGD, another using full-batch GD with a different optimizer family), it’s often best to have separate launcher scripts (scripts/train_sgd.py, scripts/train_gd.py).
- Each launcher script will define its own
fdl_flags.DEFINE_fiddle_configinstance (e.g.,--sgd_cfgin one,--gd_cfgin another). - The
default_moduleand thedefault="config:..."string for each flag can be tailored to the specific experiment type, and should point to appropriate base configurations (e.g.,configs.trainer_configs.sgd_trainer_basevs.configs.trainer_configs.gd_trainer_base).
1.4 Understanding Fiddle Flags: DEFINE_fiddle_config, default_module, default, and Name Resolution
The fdl_flags.DEFINE_fiddle_config (and DEFINE_fiddle_sweep) function is powerful but its name resolution can be subtle. Here’s how to use default_module and default effectively to minimize confusion and errors.
A. The name Argument:
- This defines the actual command-line flag, e.g.,
name="exp_cfg"creates--exp_cfg. - Convention: Choose a short, descriptive name, and be consistent across our launcher scripts (e.g., always use
--exp_cfgfor single runs,--sweep_cfgfor sweeps).
B. The default_module Argument:
Purpose: This tells Fiddle where to look for function/class names when they are not fully qualified in a
config:orfiddler:command on the CLI (or in thedefault=string, if it uses short names).Type: It can be a single Python module object or a list of module objects.
How it Works with Short Names (e.g.,
config:my_recipe):- If
default_moduleis a single module (e.g.,default_module=my_recipes_module), Fiddle will look formy_recipes_module.my_recipe. - If
default_moduleis a list of modules (e.g.,default_module=[base_module, fiddler_module]), Fiddle will trybase_module.my_recipe, thenfiddler_module.my_recipe, etc., using the first one it finds.
- If
How it Works with Partially Qualified Names (e.g.,
config:sub_module.my_recipe):- Fiddle will iterate through
default_module. For eachmodin the list, it will try to resolvemod.sub_module.my_recipe.
- Fiddle will iterate through
IMPORTANT: Interaction with Fully Qualified Names (e.g.,
config:my_project.configs.my_recipe):- If a name in a
config:orfiddler:command is already fully qualified (i.e., it’s an absolute import path likemy_project.configs.my_recipe), Fiddle will first attempt to resolve it relative to the modules indefault_moduleif the first part of the qualified name matches an attribute of a module indefault_module. This behaviour can be surprising. - If that relative resolution fails, it will then try a direct, absolute import of the fully qualified name.
- If a name in a
Recommendation for
default_module:- Be Specific: Provide the most specific module(s) that directly contain the functions we’ll commonly reference with short names.
- Avoid Ambiguity: If
module1.fooandmodule2.fooboth exist anddefault_module=[module1, module2], thenconfig:foowill resolve tomodule1.foo. Be explicit withconfig:module2.fooif that’s intended. - Use a List for Broader Scope: If our base configs are in
configs.baseand fiddlers inconfigs.fiddlers, thendefault_module=[configs.base, configs.fiddlers]allows CLI commands likeconfig:my_base_configandfiddler:my_fiddler_fn.
C. The default Argument:
Purpose: This string provides the default set of commands to apply if the flag is not provided (e.g.,
--exp_cfg=...) on the command line at all.Format: It’s a string (or list of strings for multi-string flags, though
DEFINE_fiddle_configtakes a single string which might contain multiple commands if properly formatted for the underlying flag parser, but usually it’s oneconfig:command). This string must follow the samecommand:valuesyntax as used on the CLI.IMPORTANT: Name Resolution for
defaultString:The names within the
defaultstring (e.g.,my_recipeindefault="config:my_recipe") are resolved using thedefault_moduleprovided toDEFINE_fiddle_config.Best Practice: For the
default=string, it is highly recommended to use fully qualified Python paths to our config/recipe functions to avoid any ambiguity. This makes the default behaviour independent of howdefault_modulemight be set or changed for CLI parsing convenience.Why? Fiddle internally processes this
defaultstring when the flag is defined. If it uses short names, it relies on thedefault_moduleat that definition time. Ifdefault_moduleis later changed or if the CLI resolution behaves slightly differently, the effective default can change unexpectedly. Fully qualified paths in thedefault=string are safer.
D. Understanding the “Inscrutable Error” (Could not resolve reference... available names: clear, copy, pop...)
This specific error means Fiddle was trying to find an attribute (like toy_vm_recipes) on a list object. This happens when: 1. We provide default_module=[my_actual_module_object] (a list containing one module). 2. And the default or CLI command string is something like config:my_actual_module_object.my_recipe_name. 3. Fiddle’s name resolution logic might try to see if my_actual_module_object (the string) is an attribute of the items in the default_module list. Since the item is my_actual_module_object, this can lead to a mismatch where it then tries to find my_recipe_name on the list itself if the initial attribute lookup fails or is misinterpreted.
How to Minimize Harm and Debug: 1. Be Explicit with default=: Use fully qualified paths in the default="config:..." string.
* *Your Example Corrected for Robust Default*:
```python
PHYSICS_MODEL_CFG = fdl_flags.DEFINE_fiddle_config(
name="physics_recipe_cfg",
# default_module is primarily for CLI convenience with short names
default_module=[configs.physics_specific_configs.pidm_physics_recipes, configs.physics_specific_configs],
# Use the full Python path for the default string value
default="config:configs.physics_specific_configs.pidm_physics_recipes.toy_vector_magnitude_physics_recipe",
help_string="...",
)
```
(Assuming `configs.physics_specific_configs.pidm_physics_recipes` is the actual importable path to the module containing `toy_vector_magnitude_physics_recipe`).
Simplify
default_moduleif Possible:- If most referenced functions are in a single module, pass that module directly:
default_module=my_primary_configs_module. - If using a list for
default_module, be aware of potential ambiguities if the same short names exist in multiple modules in that list.
- If most referenced functions are in a single module, pass that module directly:
Test the Defaults: Run the launcher script without any Fiddle flags to ensure the default configuration loads correctly. This is where the
temp_flag_parserworkaround in the previous response becomes relevant ifFLAG.valueisn’t directly giving us theBuildablefrom the default.Use Fully Qualified Names on CLI for Unambiguity: When in doubt or when scripting, use the full Python path in CLI commands (
--exp_cfg=config:my_project.configs.module.my_func). This is always the safest option, though more verbose.default_moduleis a convenience for interactive use or shorter commands.Inspect
FLAG.value: When debugging, always printtype(YOUR_FLAG.value)andYOUR_FLAG.valueitself.- If CLI flags are provided,
YOUR_FLAG.valueshould be anfdl.Buildable. - If no CLI flags are provided and we used
default="config:...", andYOUR_FLAG.valueis still a list of strings (e.g.,['config:your.default.recipe']or theconfig_str:eJx...form), then we need to re-process this list to get thefdl.Buildableas shown in the workaround using a temporaryFiddleFlaginstance. This indicates the.valueproperty doesn’t fully resolve string defaults when no CLI overrides are present in our Fiddle/Abseil setup.
- If CLI flags are provided,
By making the default= string fully qualified and understanding how default_module is used for resolving short names given on the CLI, we can significantly reduce these “inscrutable” resolution errors. The primary role of default_module becomes providing convenience for CLI users who prefer shorter names.
1.5 Experiment Sweeps with Submitit
For parameter sweeps, the recommended pattern is:
Launcher Script (
scripts/run_my_sweep.py):Uses
fdl_flags.DEFINE_fiddle_sweep("sweep_cfg", ...)to define the sweep parameters.Iterates through
SWEEP_CFG.value(which yieldsSweepItemobjects).For each
sweep_item:- Determines a unique
experiment_nameandrun_idfrom the sweep parameters (sweep_item.overrides_applied). - Gathers all launch-time metadata (launcher CLI, git hash, RNG seeds,
sweep_item.overrides_applied). - Serializes
sweep_item.configto a JSON string usingfdl.experimental.serialization.dump_json(). - Uses
submitit’sexecutor.submit()orexecutor.map_array()to schedule the worker function. - Passes the serialized config string and all gathered metadata as distinct arguments to the worker function.
- Determines a unique
Worker Function (in
src/mind_the_gap/business_logic/...):- Accepts the serialized config string and all metadata as arguments.
- Deserializes the config string using
fdl.experimental.serialization.load_json(). - Sets up RNGs using the provided seeds.
- Saves all received metadata (including the re-serialized Fiddle config JSON) to
metadata.jsonin the designated artifact directory for this run. - Calls
fdl.build()on the deserialized Fiddle config. - Runs the actual experiment logic.
This pattern ensures robust process isolation and clear metadata tracking for every trial in the sweep.
1.6 Command-Line Interface (New API)
Fiddle’s integration with absl.flags allows flexible configuration from the command line. Always use the new fdl_flags.DEFINE_fiddle_config API and its associated CLI syntax.
Defining the Flag (in our launcher script, e.g., scripts/your_launcher.py)
# scripts/your_launcher.py
from absl import app
import fiddle as fdl
from fiddle import absl_flags as fdl_flags
# IMPORTANT: Import ALL modules that define functions/classes
# you might want to reference by name from the CLI or in default strings.
from configs import base_configs, model_configs, data_configs, optimizer_configs
from configs import fiddlers, experiment_recipes, tags
EXP_CFG = fdl_flags.DEFINE_fiddle_config(
name="exp_cfg", # This sets the CLI flag name to --exp_cfg
default_module=[ # List of modules Fiddle will search for short names
base_configs, model_configs, data_configs, optimizer_configs,
fiddlers, experiment_recipes, tags
],
# For the default, using the FULL Python path is MOST ROBUST
default="config:configs.experiment_recipes.my_default_experiment_recipe",
help_string="Main Fiddle configuration for the experiment.",
required=False # False if a 'default' is provided
)
def main(argv):
unbuilt_cfg: fdl.Buildable = EXP_CFG.value
# ... (rest of your script) ...1.6.1 Understanding default_module and default in DEFINE_fiddle_config
This is a common source of confusion and “inscrutable errors”.
name="flag_name": This defines your CLI flag (e.g.,--flag_name).- Convention: Standardize these across your project (e.g.,
--exp_cfgfor single runs,--sweep_cfgfor sweeps).
- Convention: Standardize these across your project (e.g.,
default_module=[module1, module2, ...]:Purpose: Tells Fiddle where to look for function/class names when they are not fully qualified in a
config:orfiddler:command given on the CLI (or in thedefault=string if that string uses short names).Behavior with Short Names (e.g.,
config:my_recipe): Fiddle searchesmodule1.my_recipe, thenmodule2.my_recipe, etc., using the first match.Behavior with Partially Qualified Names (e.g.,
config:sub_module.my_recipe): Fiddle triesmodule1.sub_module.my_recipe, thenmodule2.sub_module.my_recipe, etc.Behavior with Fully Qualified Names (e.g.,
config:project.configs.my_recipe): Fiddle will first try to resolve this relative to each module indefault_moduleif the leading part matches an attribute of a module in the list (e.g., ifdefault_module=[project_configs_module]andproject_configs_modulehas an attributeconfigs, it might tryproject_configs_module.configs.my_recipe). If these relative attempts fail, it falls back to a direct absolute import ofproject.configs.my_recipe.Recommendation:
- Provide the actual imported module objects, not strings of module names.
- Be specific. If you have
configs.baseandconfigs.fiddlers, include both:default_module=[configs.base, configs.fiddlers]. - If using short names on the CLI, be mindful of potential name collisions if they exist in multiple modules in the
default_modulelist (the first one found wins).
default="config:path.to.default_recipe":- Purpose: Specifies the Fiddle command string(s) to use if the user provides no instances of this flag (e.g., no
--exp_cfg) on the command line. - IMPORTANT - Best Practice: Always use the fully qualified Python import path to your default recipe function within the
defaultstring.python default="config:my_project.configs.experiment_recipes.default_experiment_setup"This makes your default behavior robust and independent of howdefault_modulemight be configured for CLI convenience. - Internal Behavior: Fiddle processes this
defaultstring when the flag is defined. It often serializes the result into aconfig_str:...format for its internal default storage. This is normal and visible in--helpfulloutput.
- Purpose: Specifies the Fiddle command string(s) to use if the user provides no instances of this flag (e.g., no
1.6.2 Accessing the fdl.Buildable (Getting the Config Object)
After
absl.app.run(main)has processed flags,YOUR_FLAG_HOLDER.value(e.g.,EXP_CFG.value) is the property to access the finalfdl.Buildable.Typical Behavior (CLI Overrides Present): If the user provides
--exp_cfg=...on the command line,EXP_CFG.valuewill directly return the processedfdl.Buildableobject.Potential Issue (Default Value Only):
You observed that if only the
default="config:..."string is used (no--exp_cfgon CLI),EXP_CFG.valuemight return alistcontaining the command string(s) from the default, e.g.,['config:your.default.recipe'].This is the “flaky” behavior. The
FiddleFlag.valueproperty should ideally handle this transparently.Robust Workaround (if the above issue persists in your Fiddle version):
# Inside your main() function raw_flag_value = EXP_CFG.value unbuilt_cfg: fdl.Buildable if isinstance(raw_flag_value, fdl.Buildable): unbuilt_cfg = raw_flag_value elif isinstance(raw_flag_value, list) and all(isinstance(x, str) for x in raw_flag_value): print("WARNING: Flag default resolved to command strings. Re-parsing default explicitly.") # EXPERIMENT_FDL_CONFIG.default_module is the correct attribute on the FlagHolder temp_parser = fdl_flags.FiddleFlag( name="_temp_default_parser", default=None, parser=flags.ArgumentParser(), serializer=None, help_string="Internal", default_module=EXPERIMENT_FDL_CONFIG.default_module, # Correct way to get it # allow_imports=EXPERIMENT_FDL_CONFIG.allow_imports # If you use this setting ) temp_parser.parse(raw_flag_value) # Parse the list of command strings unbuilt_cfg = temp_parser.value else: raise TypeError(f"Unexpected type from flag value: {type(raw_flag_value)}") # Now, unbuilt_cfg should definitely be an fdl.Buildable exp_cfg_instance = fdl.build(unbuilt_cfg)Alternative Simpler Workaround (If the “list” problem is only for defaults): Always provide at least one
--exp_cfg=config:your_default_recipeon the command line for your default runs, and make the flagrequired=TrueinDEFINE_fiddle_config(and remove thedefault=string from the definition). This forces the CLI parsing path which consistently returns aBuildable.
1.6.3 CLI Syntax for Setting Parameters (set:)
Target Arguments, Not Internals: When using
config:my_recipe_fn, subsequentset:commands operate on the arguments ofmy_recipe_fn.If
my_recipe_fn(param1: int, sub_config: fdl.Config[AnotherClass]), then:--exp_cfg=set:param1=10(Correct)--exp_cfg=set:sub_config.attr_of_another_class=value(Correct)
Avoid paths that refer to the internal structure of objects returned by
my_recipe_fn. This was the cause of yourAttributeErrorforthermal_diffusivity.thermal_diffusivityis an argument to your recipe, and the recipe then uses it to configurePhysicsComponents.physics_solver_config.thermal_diffusivity.- Correct CLI:
--exp_cfg=set:thermal_diffusivity=0.2 - Incorrect CLI (what caused your error):
--exp_cfg=set:physics_solver_config.thermal_diffusivity=0.2(This assumesphysics_solver_configis a top-level argument to the recipe, which it is, but then you want to set an attribute of that argument directly if it’s a config object, not an attribute of the PhysicsComponents object that the recipe returns using a path from the root of PhysicsComponents.*)
- Correct CLI:
Corrected Example from Bug Report (Conceptual): Assuming
thin_plate_physics_recipetakesheat_source_cfgandtemperature_field_cfgas arguments:--physics_recipe_cfg="config:configs...thin_plate_physics_recipe" \ --physics_recipe_cfg="set:thermal_diffusivity=0.2" \ --physics_recipe_cfg="set:base_thickness_value=0.015" \ --physics_recipe_cfg="set:heat_source_cfg.center=(0.0,0.0)" \ --physics_recipe_cfg="set:temperature_field_cfg.perlin_noise_cfg.amplitude=3.0"
1.6.4 Other Commands (fiddler:, config_file:, config_str:)
- These follow the same name resolution rules via
default_moduleif short names are used. - They are applied sequentially in the order they appear on the command line for a given flag instance.
1.7 Reproducibility and Artifacts
metadata.jsonis Key: For each run, ensuremetadata.jsoninartifacts/runs/<experiment_name>/<run_id>/contains:fiddle_config_json: The output offdl.experimental.serialization.dump_json(unbuilt_cfg).launcher_cli_command: The exactsys.argvof the launcher script.git_hash: Captured at launch time.rng_seeds: A dictionary of all seeds used (Python, NumPy, PyTorch/JAX, etc.).schema_version: e.g.,"1.0". Increment if you change themetadata.jsonstructure.sweep_params_json: (If applicable) JSON string ofsweep_item.overrides_applied.
- Shared Resources: Paths to datasets, pre-trained models, etc., should be configured within your Fiddle
Buildable(e.g.,cfg.data.path = "/path/to/dataset"), not hardcoded in business logic.
1.8 Potential Gotchas & Troubleshooting
FLAG.valuefor Defaults: As noted, if only adefault="config:..."string is used withDEFINE_fiddle_configand no CLI flag is passed,FLAG.valuemight return the command string(s) as a list. The robust workaround is to re-parse this default using a temporaryFiddleFlaginstance as shown in the launcher script example.- String Argument Quoting on CLI: When using
set:path.to.str_arg="my value", ensure the string value itself is quoted if it contains spaces or special characters that the shell or Fiddle’s value parser might misinterpret:set:foo.name='"A String Value"'. - Module Resolution for
default_module: Ensure the modules passed todefault_moduleare the actual imported module objects, and that the names used inconfig:orfiddler:commands can be resolved as attributes of those modules. - Forgetting
@auto_configor.as_buildable(): If using@auto_config, remember to call.as_buildable()on the decorated function to get the Fiddle config. If you pass theauto_config-decorated function itself tofdl.Config(), Fiddle will try to configure theAutoConfigwrapper object, not your intended function.
