5
5
# the root directory of this source tree.
6
6
7
7
import importlib .resources
8
+ import json
8
9
import sys
9
10
10
11
from pydantic import BaseModel
@@ -41,16 +42,16 @@ class ApiInput(BaseModel):
41
42
42
43
def get_provider_dependencies (
43
44
config : BuildConfig | DistributionTemplate ,
44
- ) -> tuple [list [str ], list [str ], list [str ]]:
45
+ ) -> tuple [dict [ str , list [str ]], dict [ str , list [str ]], dict [ str , list [str ] ]]:
45
46
"""Get normal and special dependencies from provider configuration."""
46
47
if isinstance (config , DistributionTemplate ):
47
48
config = config .build_config ()
48
49
49
50
providers = config .distribution_spec .providers
50
51
additional_pip_packages = config .additional_pip_packages
51
52
52
- deps = []
53
- external_provider_deps = []
53
+ deps = {}
54
+ external_provider_deps = {}
54
55
registry = get_provider_registry (config )
55
56
for api_str , provider_or_providers in providers .items ():
56
57
providers_for_api = registry [Api (api_str )]
@@ -69,37 +70,86 @@ def get_provider_dependencies(
69
70
# this ensures we install the top level module for our external providers
70
71
if provider_spec .module :
71
72
if isinstance (provider_spec .module , str ):
72
- external_provider_deps .append (provider_spec .module )
73
+ external_provider_deps .setdefault ( provider_spec . provider_type , []). append (provider_spec .module )
73
74
else :
74
- external_provider_deps .extend (provider_spec .module )
75
+ external_provider_deps .setdefault ( provider_spec . provider_type , []). extend (provider_spec .module )
75
76
if hasattr (provider_spec , "pip_packages" ):
76
- deps .extend (provider_spec .pip_packages )
77
+ deps .setdefault ( provider_spec . provider_type , []). extend (provider_spec .pip_packages )
77
78
if hasattr (provider_spec , "container_image" ) and provider_spec .container_image :
78
79
raise ValueError ("A stack's dependencies cannot have a container image" )
79
80
80
- normal_deps = []
81
- special_deps = []
82
- for package in deps :
83
- if "--no-deps" in package or "--index-url" in package :
84
- special_deps .append (package )
81
+ normal_deps = {}
82
+ special_deps = {}
83
+ for provider , package in deps . items () :
84
+ if any ( "--no-deps" in s for s in package ) or any ( "--index-url" in s for s in package ) :
85
+ special_deps .setdefault ( provider , []). append (package )
85
86
else :
86
- normal_deps .append (package )
87
+ normal_deps .setdefault ( provider , []). append (package )
87
88
88
- normal_deps . extend ( additional_pip_packages or [])
89
+ normal_deps [ "default" ] = additional_pip_packages or []
89
90
90
- return list (set (normal_deps )), list (set (special_deps )), list (set (external_provider_deps ))
91
+ # Helper function to flatten and deduplicate dependencies
92
+ def flatten_and_dedup (deps_list ):
93
+ flattened = []
94
+ for item in deps_list :
95
+ if isinstance (item , list ):
96
+ flattened .extend (item )
97
+ else :
98
+ flattened .append (item )
99
+ return list (set (flattened ))
100
+
101
+ for key in normal_deps .keys ():
102
+ normal_deps [key ] = flatten_and_dedup (normal_deps [key ])
103
+ for key in special_deps .keys ():
104
+ special_deps [key ] = flatten_and_dedup (special_deps [key ])
105
+ for key in external_provider_deps .keys ():
106
+ external_provider_deps [key ] = flatten_and_dedup (external_provider_deps [key ])
107
+
108
+ return normal_deps , special_deps , external_provider_deps
91
109
92
110
93
111
def print_pip_install_help (config : BuildConfig ):
94
- normal_deps , special_deps , _ = get_provider_dependencies (config )
112
+ normal_deps , special_deps , external_provider_dependencies = get_provider_dependencies (config )
113
+ normal_deps ["default" ] += SERVER_DEPENDENCIES
114
+ cprint (
115
+ "Please install needed dependencies using the following commands:" ,
116
+ color = "yellow" ,
117
+ file = sys .stderr ,
118
+ )
119
+
120
+ for provider , deps in normal_deps .items ():
121
+ if len (deps ) == 0 :
122
+ continue
123
+ cprint (f"# Normal Dependencies for { provider } " , color = "yellow" )
124
+ cprint (f"uv pip install { ' ' .join (deps )} " , file = sys .stderr )
125
+
126
+ for provider , deps in special_deps .items ():
127
+ if len (deps ) == 0 :
128
+ continue
129
+ cprint (f"# Special Dependencies for { provider } " , color = "yellow" )
130
+ cprint (f"uv pip install { ' ' .join (deps )} " , file = sys .stderr )
131
+
132
+ for provider , deps in external_provider_dependencies .items ():
133
+ if len (deps ) == 0 :
134
+ continue
135
+ cprint (f"# External Provider Dependencies for { provider } " , color = "yellow" )
136
+ cprint (f"uv pip install { ' ' .join (deps )} " , file = sys .stderr )
137
+ print ()
138
+ return
95
139
96
140
cprint (
97
- f "Please install needed dependencies using the following commands:\n \n uv pip install { ' ' . join ( normal_deps ) } " ,
141
+ "Please install needed dependencies using the following commands:" ,
98
142
color = "yellow" ,
99
143
file = sys .stderr ,
100
144
)
101
- for special_dep in special_deps :
102
- cprint (f"uv pip install { special_dep } " , color = "yellow" , file = sys .stderr )
145
+
146
+ for provider , deps in normal_deps .items ():
147
+ cprint (f"# Normal Dependencies for { provider } " )
148
+ cprint (f"uv pip install { deps } " , color = "yellow" , file = sys .stderr )
149
+
150
+ for provider , deps in special_deps .items ():
151
+ cprint (f"# Special Dependencies for { provider } " )
152
+ cprint (f"uv pip install { deps } " , color = "yellow" , file = sys .stderr )
103
153
print ()
104
154
105
155
@@ -112,12 +162,12 @@ def build_image(
112
162
container_base = build_config .distribution_spec .container_image or "python:3.12-slim"
113
163
114
164
normal_deps , special_deps , external_provider_deps = get_provider_dependencies (build_config )
115
- normal_deps += SERVER_DEPENDENCIES
165
+ normal_deps [ "default" ] += SERVER_DEPENDENCIES
116
166
if build_config .external_apis_dir :
117
167
external_apis = load_external_apis (build_config )
118
168
if external_apis :
119
169
for _ , api_spec in external_apis .items ():
120
- normal_deps .extend (api_spec .pip_packages )
170
+ normal_deps [ "default" ] .extend (api_spec .pip_packages )
121
171
122
172
if build_config .image_type == LlamaStackImageType .CONTAINER .value :
123
173
script = str (importlib .resources .files ("llama_stack" ) / "core/build_container.sh" )
@@ -130,7 +180,7 @@ def build_image(
130
180
"--container-base" ,
131
181
container_base ,
132
182
"--normal-deps" ,
133
- " " . join (normal_deps ),
183
+ json . dumps (normal_deps ),
134
184
]
135
185
# When building from a config file (not a template), include the run config path in the
136
186
# build arguments
@@ -143,15 +193,15 @@ def build_image(
143
193
"--env-name" ,
144
194
str (image_name ),
145
195
"--normal-deps" ,
146
- " " . join (normal_deps ),
196
+ json . dumps (normal_deps ),
147
197
]
148
198
149
199
# Always pass both arguments, even if empty, to maintain consistent positional arguments
150
200
if special_deps :
151
- args .extend (["--optional-deps" , "#" . join (special_deps )])
201
+ args .extend (["--optional-deps" , json . dumps (special_deps )])
152
202
if external_provider_deps :
153
203
args .extend (
154
- ["--external-provider-deps" , "#" . join (external_provider_deps )]
204
+ ["--external-provider-deps" , json . dumps (external_provider_deps )]
155
205
) # the script will install external provider module, get its deps, and install those too.
156
206
157
207
return_code = run_command (args )
0 commit comments