77from typing import Optional , cast
88
99import rich_click as click
10+ from rich import console as rich_console
1011from rich import progress
1112
1213import truss
3536 get_dev_version_from_versions ,
3637)
3738from truss .remote .baseten .remote import BasetenRemote
38- from truss .remote .baseten .service import BasetenService
39+ from truss .remote .baseten .service import BasetenService , URLConfig
3940from truss .remote .remote_factory import USER_TRUSSRC_PATH , RemoteFactory
4041from truss .trt_llm .config_checks import (
4142 has_no_tags_trt_llm_builder ,
@@ -89,6 +90,32 @@ def _get_truss_from_directory(
8990 return load (truss_dir , config_path = config_path )
9091
9192
93+ def _start_watch_mode (
94+ target_directory : str ,
95+ model_name : str ,
96+ remote_provider : BasetenRemote ,
97+ resolved_model : dict ,
98+ resolved_versions : list ,
99+ console : "rich_console.Console" ,
100+ error_console : "rich_console.Console" ,
101+ ) -> None :
102+ if not os .path .isfile (target_directory ):
103+ remote_provider .sync_truss_to_dev_version_with_model (
104+ resolved_model , resolved_versions , target_directory , console , error_console
105+ )
106+ else :
107+ # These imports are delayed, to handle pydantic v1 envs gracefully.
108+ from truss_chains .deployment import deployment_client
109+
110+ deployment_client .watch_model (
111+ source = Path (target_directory ),
112+ model_name = model_name ,
113+ remote_provider = remote_provider ,
114+ console = console ,
115+ error_console = error_console ,
116+ )
117+
118+
92119### Top-level & utility commands. ######################################################
93120
94121
@@ -430,6 +457,7 @@ def run_python(script, target_directory):
430457 required = False ,
431458 default = False ,
432459 help = (
460+ "[DEPRECATED] Published deployments are now the default."
433461 "Push the truss as a published deployment. If no production "
434462 "deployment exists, promote the truss to production "
435463 "after deploy completes."
@@ -452,7 +480,7 @@ def run_python(script, target_directory):
452480 required = False ,
453481 help = (
454482 "Push the truss as a published deployment to the specified environment."
455- "If specified, -- publish is implied and the supplied value of --promote will be ignored."
483+ "If specified, publish is implied and the supplied value of --promote will be ignored."
456484 ),
457485)
458486@click .option (
@@ -485,8 +513,8 @@ def run_python(script, target_directory):
485513 type = str ,
486514 required = False ,
487515 help = (
488- "Name of the deployment created by the push. Can only be "
489- "used in combination with --publish or --promote ."
516+ "Name of the deployment created by the push. Cannot be "
517+ "used with --watch (development deployments) ."
490518 ),
491519)
492520@click .option (
@@ -544,6 +572,18 @@ def run_python(script, target_directory):
544572 required = False ,
545573 help = "JSON string of metadata key-value pairs." ,
546574)
575+ @click .option (
576+ "--watch" ,
577+ "watch_after_push" ,
578+ is_flag = True ,
579+ required = False ,
580+ default = False ,
581+ help = (
582+ "Deploy as a development model and watch for changes. "
583+ "Waits for deployment to complete, then starts watching for code changes "
584+ "to apply live patches. Cannot be used with --promote, --environment, or --tail."
585+ ),
586+ )
547587@common .common_options ()
548588def push (
549589 target_directory : str ,
@@ -565,13 +605,44 @@ def push(
565605 deploy_timeout_minutes : Optional [int ] = None ,
566606 provided_team_name : Optional [str ] = None ,
567607 metadata : Optional [str ] = None ,
608+ watch_after_push : bool = False ,
568609) -> None :
569610 """
570611 Pushes a truss to a TrussRemote.
571612
572613 TARGET_DIRECTORY: A Truss directory. If none, use current directory.
573614
574615 """
616+
617+ if publish :
618+ console .print (
619+ "[DEPRECATED] The --publish flag is deprecated. Published deployments are now the default." ,
620+ style = "yellow" ,
621+ )
622+
623+ # Handle --watch flag: deploys as development and then watches
624+ if watch_after_push :
625+ if promote :
626+ raise click .UsageError (
627+ "Cannot use --watch with --promote. Watch mode runs a development deployment."
628+ )
629+ if environment :
630+ raise click .UsageError (
631+ "Cannot use --watch with --environment. Watch mode runs a development deployment."
632+ )
633+ if tail :
634+ raise click .UsageError ("Cannot use --watch with --tail." )
635+ # Development deployment for watch mode
636+ publish = False
637+ wait = True
638+ else :
639+ # Default is now published deployment
640+ publish = True
641+ console .print (
642+ "Deploying as a published deployment. Use --watch for a development deployment." ,
643+ style = "green" ,
644+ )
645+
575646 tr = _get_truss_from_directory (target_directory = target_directory , config = config )
576647
577648 if tr .spec .config .resources .instance_type :
@@ -586,7 +657,7 @@ def push(
586657 and not promote
587658 ):
588659 raise click .UsageError (
589- "Truss with gRPC transport cannot be used as a development deployment. Please rerun the command with --publish or --promote ."
660+ "Truss with gRPC transport cannot be used as a development deployment. Remove --watch to deploy as a published model ."
590661 )
591662
592663 if not remote :
@@ -609,7 +680,7 @@ def push(
609680 effective_team_name = provided_team_name or RemoteFactory .get_remote_team (
610681 remote
611682 )
612- _ , team_id = resolve_model_team_name (
683+ team_name , team_id = resolve_model_team_name (
613684 remote_provider = remote_provider ,
614685 provided_team_name = effective_team_name ,
615686 existing_model_name = model_name ,
@@ -662,7 +733,7 @@ def push(
662733 # trt-llm engine builder checks
663734 if uses_trt_llm_builder (tr ):
664735 if not publish :
665- live_reload_disabled_text = "Development mode is currently not supported for trusses using TRT-LLM build flow, push as a published model using --publish "
736+ live_reload_disabled_text = "Development mode is currently not supported for trusses using TRT-LLM build flow. Remove --watch to deploy as a published model. "
666737 console .print (live_reload_disabled_text , style = "red" )
667738 sys .exit (1 )
668739
@@ -720,8 +791,7 @@ def push(
720791| iterate quickly during the deployment process. |
721792| |
722793| When you are ready to publish your deployed model as a new deployment, |
723- | pass '--publish' to the 'truss push' command. To monitor changes to your model and |
724- | rapidly iterate, run the 'truss watch' command. |
794+ | run 'truss push' without --watch. |
725795| |
726796|---------------------------------------------------------------------------------------|
727797"""
@@ -741,8 +811,6 @@ def push(
741811 if wait :
742812 start_time = time .time ()
743813 with console .status ("[bold green]Deploying..." ) as status :
744- # Poll for the deployment status until we have reached. Either ACTIVE,
745- # or a non-deploying status (in which case the deployment has failed).
746814 for deployment_status in service .poll_deployment_status ():
747815 if (
748816 timeout_seconds is not None
@@ -757,7 +825,7 @@ def push(
757825
758826 if deployment_status == ACTIVE_STATUS :
759827 console .print ("Deployment succeeded." , style = "bold green" )
760- return
828+ break
761829
762830 if deployment_status not in DEPLOYING_STATUSES :
763831 console .print (
@@ -766,6 +834,27 @@ def push(
766834 )
767835 sys .exit (1 )
768836
837+ # If --watch was used, start watching after deploy success
838+ if watch_after_push :
839+ if not isinstance (remote_provider , BasetenRemote ):
840+ raise click .UsageError (
841+ f"Watch mode is not supported for remote provider type: { type (remote_provider ).__name__ } "
842+ )
843+ bt_remote = cast (BasetenRemote , remote_provider )
844+ console .print ("Starting watch mode..." , style = "bold blue" )
845+ resolved_model , versions = resolve_model_for_watch (
846+ bt_remote , model_name , provided_team_name = team_name
847+ )
848+ _start_watch_mode (
849+ target_directory = target_directory ,
850+ model_name = model_name ,
851+ remote_provider = bt_remote ,
852+ resolved_model = resolved_model ,
853+ resolved_versions = versions ,
854+ console = console ,
855+ error_console = error_console ,
856+ )
857+
769858 elif tail and isinstance (service , BasetenService ):
770859 bt_remote = cast (BasetenRemote , remote_provider )
771860 log_watcher = ModelDeploymentLogWatcher (
@@ -862,31 +951,29 @@ def watch(
862951 # Verify dev version exists
863952 dev_version = get_dev_version_from_versions (versions )
864953 if not dev_version :
865- console .print ("❌ No development model found. Run `truss push` then try again." )
954+ console .print (
955+ "❌ No development model found. Run `truss push --watch` then try again."
956+ )
866957 sys .exit (1 )
867958
868959 # Use model_id to get service (no additional resolution needed)
869- service = remote_provider .get_service (model_identifier = ModelId (model_id ))
960+ dev_version_id = dev_version ["id" ]
961+ logs_url = URLConfig .model_logs_url (
962+ remote_provider .remote_url , model_id , dev_version_id
963+ )
870964 console .print (
871- f"🪵 View logs for your deployment at { common .format_link (service . logs_url )} "
965+ f"🪵 View logs for your development model at { common .format_link (logs_url )} "
872966 )
873967
874- if not os .path .isfile (target_directory ):
875- # Pass the resolved model to avoid re-resolution
876- remote_provider .sync_truss_to_dev_version_with_model (
877- resolved_model , versions , target_directory , console , error_console
878- )
879- else :
880- # These imports are delayed, to handle pydantic v1 envs gracefully.
881- from truss_chains .deployment import deployment_client
882-
883- deployment_client .watch_model (
884- source = Path (target_directory ),
885- model_name = model_name ,
886- remote_provider = remote_provider ,
887- console = console ,
888- error_console = error_console ,
889- )
968+ _start_watch_mode (
969+ target_directory = target_directory ,
970+ model_name = model_name ,
971+ remote_provider = remote_provider ,
972+ resolved_model = resolved_model ,
973+ resolved_versions = versions ,
974+ console = console ,
975+ error_console = error_console ,
976+ )
890977
891978
892979### Image commands. ####################################################################
0 commit comments