88import os
99import ssl
1010import subprocess
11+ import sys
1112from pathlib import Path
1213
1314import uvicorn
1415import yaml
16+ from termcolor import cprint
1517
1618from llama_stack .cli .stack .utils import ImageType
1719from llama_stack .cli .subcommand import Subcommand
18- from llama_stack .core .datatypes import StackRunConfig
20+ from llama_stack .core .datatypes import Api , Provider , StackRunConfig
21+ from llama_stack .core .distribution import get_provider_registry
1922from llama_stack .core .stack import cast_image_name_to_string , replace_env_vars
23+ from llama_stack .core .storage .datatypes import (
24+ InferenceStoreReference ,
25+ KVStoreReference ,
26+ ServerStoresConfig ,
27+ SqliteKVStoreConfig ,
28+ SqliteSqlStoreConfig ,
29+ SqlStoreReference ,
30+ StorageConfig ,
31+ )
32+ from llama_stack .core .utils .config_dirs import DISTRIBS_BASE_DIR
2033from llama_stack .core .utils .config_resolution import Mode , resolve_config_or_distro
2134from llama_stack .log import LoggingConfig , get_logger
2235
@@ -68,6 +81,12 @@ def _add_arguments(self):
6881 action = "store_true" ,
6982 help = "Start the UI server" ,
7083 )
84+ self .parser .add_argument (
85+ "--providers" ,
86+ type = str ,
87+ default = None ,
88+ help = "Run a stack with only a list of providers. This list is formatted like: api1=provider1,api1=provider2,api2=provider3. Where there can be multiple providers per API." ,
89+ )
7190
7291 def _run_stack_run_cmd (self , args : argparse .Namespace ) -> None :
7392 import yaml
@@ -93,6 +112,49 @@ def _run_stack_run_cmd(self, args: argparse.Namespace) -> None:
93112 config_file = resolve_config_or_distro (args .config , Mode .RUN )
94113 except ValueError as e :
95114 self .parser .error (str (e ))
115+ elif args .providers :
116+ provider_list : dict [str , list [Provider ]] = dict ()
117+ for api_provider in args .providers .split ("," ):
118+ if "=" not in api_provider :
119+ cprint (
120+ "Could not parse `--providers`. Please ensure the list is in the format api1=provider1,api2=provider2" ,
121+ color = "red" ,
122+ file = sys .stderr ,
123+ )
124+ sys .exit (1 )
125+ api , provider_type = api_provider .split ("=" )
126+ providers_for_api = get_provider_registry ().get (Api (api ), None )
127+ if providers_for_api is None :
128+ cprint (
129+ f"{ api } is not a valid API." ,
130+ color = "red" ,
131+ file = sys .stderr ,
132+ )
133+ sys .exit (1 )
134+ if provider_type in providers_for_api :
135+ provider = Provider (
136+ provider_type = provider_type ,
137+ provider_id = provider_type .split ("::" )[1 ],
138+ )
139+ provider_list .setdefault (api , []).append (provider )
140+ else :
141+ cprint (
142+ f"{ provider } is not a valid provider for the { api } API." ,
143+ color = "red" ,
144+ file = sys .stderr ,
145+ )
146+ sys .exit (1 )
147+ run_config = self ._generate_run_config_from_providers (providers = provider_list )
148+ config_dict = run_config .model_dump (mode = "json" )
149+
150+ # Write config to disk in providers-run directory
151+ distro_dir = DISTRIBS_BASE_DIR / "providers-run"
152+ config_file = distro_dir / "run.yaml"
153+
154+ logger .info (f"Writing generated config to: { config_file } " )
155+ with open (config_file , "w" ) as f :
156+ yaml .dump (config_dict , f , default_flow_style = False , sort_keys = False )
157+
96158 else :
97159 config_file = None
98160
@@ -214,3 +276,44 @@ def _start_ui_development_server(self, stack_server_port: int):
214276 )
215277 except Exception as e :
216278 logger .error (f"Failed to start UI development server in { ui_dir } : { e } " )
279+
280+ def _generate_run_config_from_providers (self , providers : dict [str , list [Provider ]]):
281+ apis = list (providers .keys ())
282+ distro_dir = DISTRIBS_BASE_DIR / "providers-run"
283+ # need somewhere to put the storage.
284+ os .makedirs (distro_dir , exist_ok = True )
285+ storage = StorageConfig (
286+ backends = {
287+ "kv_default" : SqliteKVStoreConfig (
288+ db_path = f"${{env.SQLITE_STORE_DIR:={ distro_dir } }}/kvstore.db" ,
289+ ),
290+ "sql_default" : SqliteSqlStoreConfig (
291+ db_path = f"${{env.SQLITE_STORE_DIR:={ distro_dir } }}/sql_store.db" ,
292+ ),
293+ },
294+ stores = ServerStoresConfig (
295+ metadata = KVStoreReference (
296+ backend = "kv_default" ,
297+ namespace = "registry" ,
298+ ),
299+ inference = InferenceStoreReference (
300+ backend = "sql_default" ,
301+ table_name = "inference_store" ,
302+ ),
303+ conversations = SqlStoreReference (
304+ backend = "sql_default" ,
305+ table_name = "openai_conversations" ,
306+ ),
307+ prompts = KVStoreReference (
308+ backend = "kv_default" ,
309+ namespace = "prompts" ,
310+ ),
311+ ),
312+ )
313+
314+ return StackRunConfig (
315+ image_name = "providers-run" ,
316+ apis = apis ,
317+ providers = providers ,
318+ storage = storage ,
319+ )
0 commit comments