77
88from data_designer .cli .ui import (
99 BACK ,
10+ confirm_action ,
1011 console ,
12+ display_config_preview ,
1113 print_error ,
1214 print_header ,
1315 print_info ,
16+ print_navigation_tip ,
1417 print_success ,
18+ print_warning ,
1519 prompt_text_input ,
1620 select_with_arrows ,
1721)
@@ -62,14 +66,62 @@ def models_command(
6266 raise typer .Exit (1 )
6367
6468 print_info (f"Configuration will be saved to: { config_dir } " )
65- console .print ()
69+ print_navigation_tip ()
70+
71+ # Check for existing configuration
72+ model_path = get_model_config_path (config_dir )
73+ existing_config = None
74+ mode = "create" # "create", "add", or "rewrite"
75+
76+ if model_path .exists ():
77+ try :
78+ existing_config = load_config_file (model_path )
79+ except Exception as e :
80+ print_warning (f"Could not load existing configuration: { e } " )
81+ print_info ("Starting with new configuration" )
82+ else :
83+ # Successfully loaded existing config
84+ print_info (
85+ f"Found existing model configuration with { len (existing_config .get ('model_configs' , []))} model(s)"
86+ )
87+ console .print ()
88+
89+ # Show existing configuration
90+ display_config_preview (existing_config , "Current Configuration" )
91+ console .print ()
92+
93+ # Ask what to do
94+ action_options = {
95+ "add" : "Add more models to existing configuration" ,
96+ "rewrite" : "Rewrite the entire configuration" ,
97+ "exit" : "Exit without making changes" ,
98+ }
99+ action = select_with_arrows (
100+ action_options ,
101+ "What would you like to do?" ,
102+ default_key = "add" ,
103+ allow_back = False ,
104+ )
105+
106+ if action is None or action == "exit" :
107+ print_info ("No changes made to configuration" )
108+ raise typer .Exit (0 )
109+ elif action == "add" :
110+ mode = "add"
111+ elif action == "rewrite" :
112+ mode = "rewrite"
66113
67114 # Run the model configuration wizard
68- model_config = configure_models (available_providers )
115+ model_config = configure_models (available_providers , existing_config if mode == "add" else None )
69116 if model_config is None :
70117 print_error ("Model configuration cancelled" )
71118 raise typer .Exit (1 )
72119
120+ # Check if config actually changed (when adding to existing)
121+ if mode == "add" and model_config == existing_config :
122+ print_info ("No changes made to configuration" )
123+ raise typer .Exit (0 )
124+
73125 # Save configuration
74126 try :
75127 ensure_config_dir_exists (config_dir )
@@ -81,59 +133,51 @@ def models_command(
81133 raise typer .Exit (1 )
82134
83135
84- def configure_models (available_providers : list [str ]) -> dict | None :
136+ def configure_models (available_providers : list [str ], existing_config : dict | None = None ) -> dict | None :
85137 """Interactive configuration for model configs with back navigation.
86138
87139 Args:
88140 available_providers: List of available provider names
141+ existing_config: Optional existing configuration to add to
89142
90143 Returns:
91144 Model configuration dictionary, or None if cancelled
92145 """
93146 # Step-based state machine for back navigation
94- step = "num_models"
95- num_models = 1
147+ step = "model_alias"
96148 model_configs : list [dict ] = []
97- model_idx = 0
98149
99- # Storage for model data as we build it
100- current_model : dict = {}
150+ # If we have existing config, load the models
151+ num_existing = 0
152+ if existing_config :
153+ model_configs = existing_config .get ("model_configs" , []).copy ()
154+ num_existing = len (model_configs )
155+ print_info (f"Adding to existing { num_existing } model(s)" )
156+ console .print ()
101157
102- while True :
103- if step == "num_models" :
104- # Ask how many models
105- result = prompt_text_input (
106- "How many models do you want to configure? (1-10)" ,
107- default = "1" ,
108- validator = lambda x : validate_positive_int (x ) if x else (True , None ),
109- allow_back = False , # First step, can't go back
110- )
158+ # Track how many NEW models we've added (for numbering and back navigation)
159+ new_models_count = 0
111160
112- if result is None :
113- return None
161+ # Storage for model data as we build it
162+ current_model : dict = {}
114163
115- num_models = int (result ) if result else 1
116- num_models = min (max (num_models , 1 ), 10 ) # Clamp to 1-10
117- model_idx = 0
118- model_configs = []
119- step = "model_alias"
120-
121- elif step == "model_alias" :
122- if model_idx >= num_models :
123- # Done with all models
124- step = "done"
125- continue
164+ # History stack for proper back navigation
165+ # Each entry: (step_name, model_data)
166+ history : list [tuple [str , dict ]] = []
126167
168+ while True :
169+ if step == "model_alias" :
127170 console .print ()
128- print_info (f"Configuring model { model_idx + 1 } /{ num_models } " )
171+ total_count = num_existing + new_models_count + 1
172+ print_info (f"Configuring model { total_count } " )
129173
130174 # Get existing model aliases for validation
131175 model_aliases = {m ["alias" ] for m in model_configs }
132176
133177 # Model alias
134178 result = prompt_text_input (
135179 "Model alias (used in your configs)" ,
136- default = "llama-3-70b" if model_idx == 0 else current_model .get ("alias" ),
180+ default = "llama-3-70b" if new_models_count == 0 and num_existing == 0 else current_model .get ("alias" ),
137181 validator = lambda x , aliases = model_aliases : (
138182 (False , "Model alias must not be empty" )
139183 if not x
@@ -147,26 +191,40 @@ def configure_models(available_providers: list[str]) -> dict | None:
147191 if result is None :
148192 return None
149193 elif result is BACK :
150- # Go back to previous model or num_models
151- if model_idx > 0 and len (model_configs ) > 0 :
152- # Go back to previous model
153- model_idx -= 1
154- current_model = model_configs .pop ()
155- step = "model_max_tokens" # Go to last step of previous model
194+ # Go back using history if available
195+ if history :
196+ # Pop from history and restore state to edit a completed model
197+ prev_step , prev_model = history .pop ()
198+ # Remove the last model from the list so we can re-add it after editing
199+ if len (model_configs ) > num_existing :
200+ model_configs .pop ()
201+ new_models_count -= 1
202+ current_model = prev_model
203+ step = prev_step
156204 continue
157205 else :
158- # First model, go back to num_models question
159- step = "num_models"
160- continue
206+ # No history - check if there are existing models
207+ if num_existing > 0 :
208+ # There are existing models, so canceling is OK
209+ if confirm_action ("Discard the new models?" , default = False ):
210+ return existing_config # Return the original config unchanged
211+ continue
212+ else :
213+ # No existing models, confirm complete cancellation
214+ if confirm_action ("Cancel all model configuration?" , default = False ):
215+ return None
216+ continue
161217
162218 current_model = {"alias" : result }
163219 step = "model_id"
164220
165221 elif step == "model_id" :
166222 # Model ID
167223 result = prompt_text_input (
168- "Model ID (e.g., meta/llama-3.3-70b-instruct)" ,
169- default = "meta/llama-3.3-70b-instruct" if model_idx == 0 else current_model .get ("model" ),
224+ "Model ID" ,
225+ default = "meta/llama-3.3-70b-instruct"
226+ if new_models_count == 0 and num_existing == 0
227+ else current_model .get ("model" ),
170228 validator = lambda x : (False , "Model ID is required" ) if not x else (True , None ),
171229 allow_back = True ,
172230 )
@@ -216,7 +274,7 @@ def configure_models(available_providers: list[str]) -> dict | None:
216274 print_info ("Inference Parameters" )
217275
218276 result = prompt_text_input (
219- f"Temperature ({ MIN_TEMPERATURE } -{ MAX_TEMPERATURE } )" ,
277+ f"Temperature <dim> ({ MIN_TEMPERATURE } -{ MAX_TEMPERATURE } )</dim> " ,
220278 default = "0.7" ,
221279 validator = lambda x : validate_numeric_range (x , MIN_TEMPERATURE , MAX_TEMPERATURE ) if x else (True , None ),
222280 allow_back = True ,
@@ -235,7 +293,7 @@ def configure_models(available_providers: list[str]) -> dict | None:
235293 elif step == "model_top_p" :
236294 # Top P
237295 result = prompt_text_input (
238- f"Top P ({ MIN_TOP_P } -{ MAX_TOP_P } )" ,
296+ f"Top P <dim> ({ MIN_TOP_P } -{ MAX_TOP_P } )</dim> " ,
239297 default = "0.9" ,
240298 validator = lambda x : validate_numeric_range (x , MIN_TOP_P , MAX_TOP_P ) if x else (True , None ),
241299 allow_back = True ,
@@ -254,7 +312,7 @@ def configure_models(available_providers: list[str]) -> dict | None:
254312 elif step == "model_max_tokens" :
255313 # Max tokens
256314 result = prompt_text_input (
257- "Max tokens (press Enter for default: 2048) " ,
315+ "Max tokens" ,
258316 default = "2048" ,
259317 validator = lambda x : validate_positive_int (x ) if x else (True , None ),
260318 allow_back = True ,
@@ -274,13 +332,53 @@ def configure_models(available_providers: list[str]) -> dict | None:
274332 try :
275333 ModelConfig .model_validate (current_model )
276334 model_configs .append (current_model )
277- model_idx += 1
335+ new_models_count += 1
336+ # Save to history before moving on
337+ history .append (("model_max_tokens" , current_model .copy ()))
278338 current_model = {}
279- step = "model_alias " # Move to next model or finish
339+ step = "add_another " # Ask if they want to add another model
280340 except Exception as e :
281341 print_error (f"Invalid model configuration: { e } " )
282342 return None
283343
344+ elif step == "add_another" :
345+ # Ask if user wants to add another model
346+ console .print ()
347+
348+ # Create options for selection with back support
349+ add_another_options = {
350+ "yes" : "Add another model" ,
351+ "no" : "Finish configuring models" ,
352+ }
353+ result = select_with_arrows (
354+ add_another_options ,
355+ "Would you like to add another model?" ,
356+ default_key = "no" ,
357+ allow_back = True ,
358+ )
359+
360+ if result is None :
361+ return None
362+ elif result is BACK :
363+ # Go back to the last model's max_tokens step
364+ if history :
365+ prev_step , prev_model = history .pop ()
366+ # Remove the last model so we can re-add it after editing
367+ if len (model_configs ) > num_existing :
368+ model_configs .pop ()
369+ new_models_count -= 1
370+ current_model = prev_model
371+ step = "model_max_tokens"
372+ continue
373+ elif result == "yes" :
374+ step = "model_alias"
375+ else : # "no"
376+ step = "done"
377+
284378 elif step == "done" :
379+ if len (model_configs ) == 0 :
380+ print_error ("No models configured" )
381+ return None
382+
285383 config = {"model_configs" : model_configs }
286384 return config
0 commit comments