@@ -711,6 +711,19 @@ def run_agent(self, args):
711711
712712 self .print_header (f"effGen v{ __version__ } - Running Task" )
713713
714+ # Resolve the model once so the preset and plain paths agree. With no
715+ # -m/--model, mirror `quickstart`: prefer a detected cheap cloud model,
716+ # else a small local model — and say why, so the choice is never a silent
717+ # surprise (a paid cloud call or a multi-GB local download).
718+ if args .model :
719+ run_model = args .model
720+ _preflight_model_hint (self , run_model , provider )
721+ else :
722+ run_model , _sugg_provider , _sugg_reason = _quickstart_suggest_model ()
723+ if provider is None and _sugg_provider :
724+ provider = _sugg_provider
725+ self .print (f"Using model { run_model } ({ _sugg_reason } ); override with -m/--model." )
726+
714727 agent = None
715728 try :
716729 # Load configuration if provided
@@ -728,7 +741,7 @@ def run_agent(self, args):
728741 # Use preset if specified
729742 if getattr (args , 'preset' , None ):
730743 from effgen .presets import create_agent as _create_preset_agent
731- model_id = args . model or "Qwen/Qwen2.5-3B-Instruct"
744+ model_id = run_model
732745 self .print (f"Using preset: { args .preset } " )
733746 _preset_overrides = {"provider" : provider } if provider else {}
734747 agent = _create_preset_agent (
@@ -777,7 +790,7 @@ def run_agent(self, args):
777790 # Create agent configuration
778791 agent_config = AgentConfig (
779792 name = args .name or "cli-agent" ,
780- model = args . model or "Qwen/Qwen2.5-3B-Instruct" ,
793+ model = run_model ,
781794 provider = provider ,
782795 tools = tools ,
783796 system_prompt = args .system_prompt or config .get ("system_prompt" ,
@@ -3786,6 +3799,40 @@ def _quickstart_suggest_model() -> tuple[str, str | None, str]:
37863799 return _QUICKSTART_LOCAL_MODEL , None , "no cloud key found — using a small local model"
37873800
37883801
3802+ def _preflight_model_hint (cli : "CLIInterface" , model_id : str , provider : str | None ) -> None :
3803+ """Surface a clean "did you mean" for an unknown model id, once, up front.
3804+
3805+ When the user passes an explicit ``-m`` id that isn't in the local catalog,
3806+ a high-confidence typo (e.g. ``gpt-5-nanoo``) otherwise only reveals itself
3807+ mid-run as a provider 404 wall. This checks the local catalog first and, if
3808+ the id is unknown but has near matches, prints a single tidy suggestion line.
3809+ It never blocks — the catalog can be stale, so an unknown-but-real new id is
3810+ allowed through; we only inform.
3811+ """
3812+ try :
3813+ from effgen .models import _catalog
3814+
3815+ _bare = model_id .split (":" , 1 )[- 1 ]
3816+ # Local / HF-hub ids ("org/model") are resolved by download, not via the
3817+ # cloud catalog, so a catalog miss there is meaningless — never warn on a
3818+ # legitimate local model (e.g. meta-llama/Llama-3.2-3B-Instruct). The hint
3819+ # targets slash-free cloud chat ids, where a typo is the likely cause.
3820+ if "/" in _bare :
3821+ return
3822+ if _catalog .lookup (model_id , provider ) is not None :
3823+ return
3824+ alts = _catalog .nearest_alternatives (model_id , provider , n = 3 )
3825+ if not alts :
3826+ return
3827+ names = ", " .join (r .id for r in alts )
3828+ cli .print (
3829+ f"Note: '{ _bare } ' isn't in the local catalog. Did you mean: { names } ? "
3830+ "Proceeding anyway — run 'effgen models refresh' if it's a new id."
3831+ )
3832+ except Exception : # noqa: BLE001 - a hint must never break the run
3833+ pass
3834+
3835+
37893836def _handle_quickstart_command (args , cli : "CLIInterface" ) -> int :
37903837 """Handle 'effgen quickstart' / 'effgen tutorial' — a guided first run.
37913838
@@ -3903,17 +3950,19 @@ def _handle_quickstart_command(args, cli: "CLIInterface") -> int:
39033950 return 1
39043951
39053952 # --- 4. Show the trace -------------------------------------------
3906- if response .execution_trace :
3907- cli .print_header ("What the agent did" )
3908- for i , step in enumerate (response .execution_trace , 1 ):
3909- action = step .get ("action" , step .get ("tool" , "" ))
3910- observation = step .get ("observation" , step .get ("output" , "" ))
3911- if action :
3912- cli .print (f" { i } . used [cyan]{ action } [/cyan] → { str (observation )[:80 ]} "
3913- if cli .console else
3914- f" { i } . used { action } -> { str (observation )[:80 ]} " )
3915- if not any (s .get ("action" ) or s .get ("tool" ) for s in response .execution_trace ):
3916- cli .print (" (answered directly, no tools needed)" )
3953+ # The agent's execution_trace is a list of event dicts (type/message/
3954+ # data) — not a ReAct action/observation shape — so render it with the
3955+ # shared formatter that understands those events. Hand-reading
3956+ # ``step["action"]`` here used to miss every tool call and wrongly print
3957+ # "answered directly" even when a tool ran (and tool_calls reported it).
3958+ cli .print_header ("What the agent did" )
3959+ _trace_lines = _progress .execution_trace_lines (response .execution_trace )
3960+ if _trace_lines :
3961+ for _style , _text in _trace_lines :
3962+ if cli .console :
3963+ cli .console .print (f" [{ _style } ]{ _text } [/{ _style } ]" )
3964+ else :
3965+ cli .print (f" { _text } " )
39173966 else :
39183967 cli .print (" (answered directly, no tools needed)" )
39193968
0 commit comments