@@ -12,13 +12,12 @@ import (
12
12
"github.com/DefangLabs/defang/src/pkg/cli/client"
13
13
"github.com/DefangLabs/defang/src/pkg/term"
14
14
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
15
- "github.com/compose-spec/compose-go/v2/types"
16
15
composeTypes "github.com/compose-spec/compose-go/v2/types"
17
16
)
18
17
19
18
const RAILPACK = "*Railpack"
20
19
21
- func FixupServices (ctx context.Context , provider client.Provider , project * types .Project , upload UploadMode ) error {
20
+ func FixupServices (ctx context.Context , provider client.Provider , project * composeTypes .Project , upload UploadMode ) error {
22
21
// Preload the current config so we can detect which environment variables should be passed as "secrets"
23
22
config , err := provider .ListConfig (ctx , & defangv1.ListConfigsRequest {Project : project .Name })
24
23
if err != nil {
@@ -69,6 +68,12 @@ func FixupServices(ctx context.Context, provider client.Provider, project *types
69
68
project .Services [svccfg .Name ] = svccfg
70
69
}
71
70
71
+ for name , model := range project .Models {
72
+ model .Name = name // ensure the model has a name
73
+ svccfg := fixupModel (model , project )
74
+ project .Services [svccfg .Name ] = * svccfg
75
+ }
76
+
72
77
svcNameReplacer := NewServiceNameReplacer (provider , project )
73
78
74
79
for _ , svccfg := range project .Services {
@@ -189,17 +194,17 @@ func parsePortString(port string) (uint32, error) {
189
194
}
190
195
}
191
196
192
- func fixupLLM (svccfg * types .ServiceConfig ) {
197
+ func fixupLLM (svccfg * composeTypes .ServiceConfig ) {
193
198
image := getImageRepo (svccfg .Image )
194
199
if strings .HasSuffix (image , "/openai-access-gateway" ) && len (svccfg .Ports ) == 0 {
195
200
// HACK: we must have at least one host port to get a CNAME for the service
196
201
var port uint32 = 80
197
202
term .Debugf ("service %q: adding LLM host port %d" , svccfg .Name , port )
198
- svccfg .Ports = []types .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
203
+ svccfg .Ports = []composeTypes .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
199
204
}
200
205
}
201
206
202
- func fixupPostgresService (svccfg * types .ServiceConfig , provider client.Provider , upload UploadMode ) error {
207
+ func fixupPostgresService (svccfg * composeTypes .ServiceConfig , provider client.Provider , upload UploadMode ) error {
203
208
if _ , ok := provider .(* client.PlaygroundProvider ); ok && upload != UploadModeEstimate {
204
209
term .Warnf ("service %q: managed postgres is not supported in the Playground; consider using BYOC (https://s.defang.io/byoc)" , svccfg .Name )
205
210
}
@@ -215,12 +220,12 @@ func fixupPostgresService(svccfg *types.ServiceConfig, provider client.Provider,
215
220
}
216
221
}
217
222
term .Debugf ("service %q: adding postgres host port %d" , svccfg .Name , port )
218
- svccfg .Ports = []types .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
223
+ svccfg .Ports = []composeTypes .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
219
224
}
220
225
return nil
221
226
}
222
227
223
- func fixupMongoService (svccfg * types .ServiceConfig , provider client.Provider , upload UploadMode ) error {
228
+ func fixupMongoService (svccfg * composeTypes .ServiceConfig , provider client.Provider , upload UploadMode ) error {
224
229
if _ , ok := provider .(* client.PlaygroundProvider ); ok && upload != UploadModeEstimate {
225
230
term .Warnf ("service %q: managed mongodb is not supported in the Playground; consider using BYOC (https://s.defang.io/byoc)" , svccfg .Name )
226
231
}
@@ -250,12 +255,12 @@ func fixupMongoService(svccfg *types.ServiceConfig, provider client.Provider, up
250
255
break // done
251
256
}
252
257
term .Debugf ("service %q: adding mongodb host port %d" , svccfg .Name , port )
253
- svccfg .Ports = []types .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
258
+ svccfg .Ports = []composeTypes .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
254
259
}
255
260
return nil
256
261
}
257
262
258
- func fixupRedisService (svccfg * types .ServiceConfig , provider client.Provider , upload UploadMode ) error {
263
+ func fixupRedisService (svccfg * composeTypes .ServiceConfig , provider client.Provider , upload UploadMode ) error {
259
264
if _ , ok := provider .(* client.PlaygroundProvider ); ok && upload != UploadModeEstimate {
260
265
term .Warnf ("service %q: Managed redis is not supported in the Playground; consider using BYOC (https://s.defang.io/byoc)" , svccfg .Name )
261
266
}
@@ -275,55 +280,102 @@ func fixupRedisService(svccfg *types.ServiceConfig, provider client.Provider, up
275
280
}
276
281
}
277
282
term .Debugf ("service %q: adding redis host port %d" , svccfg .Name , port )
278
- svccfg .Ports = []types .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
283
+ svccfg .Ports = []composeTypes .ServicePortConfig {{Target : port , Mode : Mode_HOST , Protocol : Protocol_TCP }}
279
284
}
280
285
return nil
281
286
}
282
287
283
- func fixupModelProvider (svccfg * types.ServiceConfig , project * types.Project ) {
284
- // Declare a private network for the model provider
285
- const modelProviderNetwork = "model_provider_private"
288
+ // Declare a private network for the model provider
289
+ const modelProviderNetwork = "model_provider_private"
290
+
291
+ func fixupModel (model composeTypes.ModelConfig , project * composeTypes.Project ) * composeTypes.ServiceConfig {
292
+ svccfg := & composeTypes.ServiceConfig {
293
+ Name : model .Name ,
294
+ Extensions : model .Extensions ,
295
+ }
296
+ makeAccessGatewayService (svccfg , project , model .Model ) // TODO: pass other model options too
297
+ return svccfg
298
+ }
299
+
300
+ func fixupModelProvider (svccfg * composeTypes.ServiceConfig , project * composeTypes.Project ) {
301
+ var model string
302
+ if modelVals := svccfg .Provider .Options ["model" ]; len (modelVals ) == 1 {
303
+ model = modelVals [0 ]
304
+ }
305
+ makeAccessGatewayService (svccfg , project , model )
306
+ }
286
307
308
+ func makeAccessGatewayService (svccfg * composeTypes.ServiceConfig , project * composeTypes.Project , model string ) {
287
309
// Local Docker sets [SERVICE]_URL and [SERVICE]_MODEL environment variables on the dependent services
288
310
envName := strings .ToUpper (svccfg .Name ) // TODO: handle characters that are not allowed in env vars, like '-'
289
- urlEnv := envName + "_URL"
311
+ endpointEnvVar := envName + "_URL"
290
312
urlVal := "http://" + svccfg .Name + "/api/v1/"
291
- modelEnvKey := envName + "_MODEL"
292
- modelVals := svccfg .Provider .Options ["model" ]
313
+ modelEnvVar := envName + "_MODEL"
293
314
294
315
empty := ""
295
- // svccfg.Deploy.Resources.Reservations.Limits = &types .Resources{} TODO: avoid memory limits warning
316
+ // svccfg.Deploy.Resources.Reservations.Limits = &composeTypes .Resources{} TODO: avoid memory limits warning
296
317
if svccfg .Environment == nil {
297
- svccfg .Environment = types .MappingWithEquals {}
318
+ svccfg .Environment = composeTypes .MappingWithEquals {}
298
319
}
299
320
if _ , exists := svccfg .Environment ["OPENAI_API_KEY" ]; ! exists {
300
321
svccfg .Environment ["OPENAI_API_KEY" ] = & empty // disable auth; see https://github.com/DefangLabs/openai-access-gateway/pull/5
301
322
}
302
- // svccfg.HealthCheck = &types .ServiceHealthCheckConfig{} TODO: add healthcheck
323
+ // svccfg.HealthCheck = &composeTypes .ServiceHealthCheckConfig{} TODO: add healthcheck
303
324
svccfg .Image = "defangio/openai-access-gateway"
304
325
if svccfg .Networks == nil {
305
326
// New compose-go versions do not create networks for "provider:" services, so we need to create it here
306
- svccfg .Networks = make (map [string ]* types .ServiceNetworkConfig )
327
+ svccfg .Networks = make (map [string ]* composeTypes .ServiceNetworkConfig )
307
328
} else {
308
329
delete (svccfg .Networks , "default" ) // remove the default network
309
330
}
310
331
svccfg .Networks [modelProviderNetwork ] = nil
311
- svccfg .Ports = []types .ServicePortConfig {{Target : 80 , Mode : Mode_HOST , Protocol : Protocol_TCP }}
332
+ svccfg .Ports = []composeTypes .ServicePortConfig {{Target : 80 , Mode : Mode_HOST , Protocol : Protocol_TCP }}
312
333
svccfg .Provider = nil // remove "provider:" because current backend will not accept it
313
- project .Networks [modelProviderNetwork ] = types .NetworkConfig {Name : modelProviderNetwork }
334
+ project .Networks [modelProviderNetwork ] = composeTypes .NetworkConfig {Name : modelProviderNetwork }
314
335
315
- // Set environment variables (url and model) for any service that depends on the provider pseudo service
336
+ // Set environment variables (url and model) for any service that depends on the model
316
337
for _ , dependency := range project .Services {
317
338
if _ , ok := dependency .DependsOn [svccfg .Name ]; ok {
318
339
if dependency .Environment == nil {
319
- dependency .Environment = make (types.MappingWithEquals )
340
+ dependency .Environment = make (composeTypes.MappingWithEquals )
341
+ }
342
+ dependency .Networks [modelProviderNetwork ] = nil
343
+ if _ , ok := dependency .Environment [endpointEnvVar ]; ! ok {
344
+ dependency .Environment [endpointEnvVar ] = & urlVal
345
+ }
346
+ if _ , ok := dependency .Environment [modelEnvVar ]; ! ok && model != "" {
347
+ dependency .Environment [modelEnvVar ] = & model
348
+ }
349
+ }
350
+
351
+ if modelDep , ok := dependency .Models [svccfg .Name ]; ok {
352
+ endpointVar := endpointEnvVar
353
+ if modelDep != nil && modelDep .EndpointVariable != "" {
354
+ endpointVar = modelDep .EndpointVariable
355
+ }
356
+ modelVar := modelEnvVar
357
+ if modelDep != nil && modelDep .ModelVariable != "" {
358
+ modelVar = modelDep .ModelVariable
359
+ }
360
+ if dependency .Environment == nil {
361
+ dependency .Environment = make (composeTypes.MappingWithEquals )
320
362
}
321
363
dependency .Networks [modelProviderNetwork ] = nil
322
- if _ , ok := dependency .Environment [urlEnv ]; ! ok {
323
- dependency .Environment [urlEnv ] = & urlVal
364
+ if _ , ok := dependency .Environment [endpointVar ]; ! ok {
365
+ dependency .Environment [endpointVar ] = & urlVal
366
+ }
367
+ if _ , ok := dependency .Environment [modelVar ]; ! ok && model != "" {
368
+ dependency .Environment [modelVar ] = & model
324
369
}
325
- if _ , ok := dependency .Environment [modelEnvKey ]; ! ok && len (modelVals ) == 1 {
326
- dependency .Environment [modelEnvKey ] = & modelVals [0 ]
370
+ // If the model is not already declared as a dependency, add it
371
+ if _ , ok := dependency .DependsOn [svccfg .Name ]; ! ok {
372
+ if dependency .DependsOn == nil {
373
+ dependency .DependsOn = make (map [string ]composeTypes.ServiceDependency )
374
+ }
375
+ dependency .DependsOn [svccfg .Name ] = composeTypes.ServiceDependency {
376
+ Condition : composeTypes .ServiceConditionStarted ,
377
+ Required : true ,
378
+ }
327
379
}
328
380
}
329
381
}
0 commit comments