Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/e2e/system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pipeline:
start:
aerospike:
action: docker:run
image: 'aerospike/aerospike-server'
image: 'aerospike/aerospike-server:3.16.0.6'
name: mly_aero
ports:
3000: 3000
Expand Down
56 changes: 54 additions & 2 deletions service/config/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type Model struct {
ID string
Debug bool

// Platform specifies the model platform (tensorflow, triton)
// Defaults to "tensorflow" for backward compatibility
Platform string `json:",omitempty" yaml:",omitempty"`

// Location is the path the model will be copied to.
Location string `json:",omitempty" yaml:",omitempty"`

Expand Down Expand Up @@ -52,6 +56,9 @@ type Model struct {
// All requests are eligible to be logged.
Stream *config.Stream `json:",omitempty" yaml:",omitempty"`

// Triton configuration for Triton Inference Server models
Triton *TritonConfig `json:",omitempty" yaml:",omitempty"`

// Modified shows the state of the model files.
Modified *Modified `json:",omitempty" yaml:",omitempty"`

Expand Down Expand Up @@ -122,9 +129,54 @@ func (m *Model) Validate() error {
return fmt.Errorf("model.ID was empty")
}

if m.URL == "" {
return fmt.Errorf("model.URL was empty")
// Platform-specific validation
platform := m.GetPlatform()
switch platform {
case "tensorflow":
if m.URL == "" {
return fmt.Errorf("tensorflow model %s requires URL", m.ID)
}
case "triton":
// Triton models require Triton configuration
if m.Triton == nil {
return fmt.Errorf("triton model %s requires Triton configuration", m.ID)
}
if err := m.Triton.Validate(); err != nil {
Comment thread
dchoi-viant marked this conversation as resolved.
return fmt.Errorf("triton model %s config invalid: %w", m.ID, err)
}
// For Triton models, URL is optional (used only for validation placeholder)
default:
return fmt.Errorf("unsupported platform '%s' for model %s (supported: tensorflow, triton)", platform, m.ID)
}

return nil
}

// TritonConfig represents Triton Inference Server specific configuration
type TritonConfig struct {
ServerURL string `json:",omitempty" yaml:",omitempty"` // Triton server URL (e.g., "http://localhost:8000")
Comment thread
dchoi-viant marked this conversation as resolved.
Outdated
ModelName string `json:",omitempty" yaml:",omitempty"` // Model name in Triton
Version string `json:",omitempty" yaml:",omitempty"` // Model version (defaults to "1")
Timeout int `json:",omitempty" yaml:",omitempty"` // HTTP timeout in seconds
Comment thread
dchoi-viant marked this conversation as resolved.
Outdated
}

// Validate validates Triton configuration
Comment thread
dchoi-viant marked this conversation as resolved.
Outdated
func (t *TritonConfig) Validate() error {
if t.ServerURL == "" {
return fmt.Errorf("Triton ServerURL is required")
}
if t.ModelName == "" {
return fmt.Errorf("Triton ModelName is required")
}
// Version defaults to "1" if not specified, so we don't require it
// Timeout defaults to reasonable value if not specified
return nil
}

// GetPlatform returns the platform with default to "tensorflow" for backward compatibility
func (m *Model) GetPlatform() string {
if m.Platform == "" {
return "tensorflow"
}
return m.Platform
}
28 changes: 25 additions & 3 deletions service/endpoint/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,31 @@ func Build(mux *http.ServeMux, config *Config, datastores map[string]*datastore.
mstart := time.Now()

log.Printf("[%s] model loading", model.ID)

// Validate model configuration first
if validateErr := model.Validate(); validateErr != nil {
log.Printf("[%s] ERROR: Model validation failed: %v", model.ID, validateErr)
lock.Lock()
Comment thread
dchoi-viant marked this conversation as resolved.
err = fmt.Errorf("model %s validation failed: %w", model.ID, validateErr)
lock.Unlock()
return
}
log.Printf("[%s] Model configuration validated successfully", model.ID)

e := func() error {
tfService := tfmodel.NewService(model, fs, metrics, sema, cfge.MaxEvaluatorWait)
modelSrv, err := service.New(context.Background(), model, tfService, fs, metrics, datastores, serviceOpts...)
var modelSrv *service.Service
var err error

// Use platform router if platform is specified, otherwise fall back to legacy TensorFlow
if model.Platform != "" {
log.Printf("[%s] Using platform-specific service creation for platform: %s", model.ID, model.Platform)
modelSrv, err = service.NewWithPlatform(context.Background(), model, fs, metrics, datastores, sema, cfge.MaxEvaluatorWait, serviceOpts...)
} else {
log.Printf("[%s] Using legacy TensorFlow service creation (backward compatibility)", model.ID)
// Legacy path for backward compatibility
tfService := tfmodel.NewService(model, fs, metrics, sema, cfge.MaxEvaluatorWait)
modelSrv, err = service.New(context.Background(), model, tfService, fs, metrics, datastores, serviceOpts...)
}

if err != nil {
return fmt.Errorf("failed to create service for model:%v, err:%w", model.ID, err)
Expand Down Expand Up @@ -139,7 +161,7 @@ func Build(mux *http.ServeMux, config *Config, datastores map[string]*datastore.
lock.Unlock()
}

log.Printf("[%s] model loaded (%s)", model.ID, time.Now().Sub(mstart))
log.Printf("[%s] model loaded (%s)", model.ID, time.Since(mstart))
}(m)
}

Expand Down
43 changes: 43 additions & 0 deletions service/platform/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package platform

import (
"fmt"
"time"

"github.com/viant/afs"
"github.com/viant/gmetric"
"github.com/viant/mly/service/config"
"github.com/viant/mly/service/tfmodel"
"golang.org/x/sync/semaphore"
)

// CreateEvaluator creates the appropriate platform evaluator based on the model configuration
func CreateEvaluator(cfg *config.Model, fs afs.Service, metrics *gmetric.Service, sema *semaphore.Weighted, maxEvaluatorWait time.Duration) (PlatformEvaluator, ModelPlatform, error) {
platform := cfg.GetPlatform()

switch platform {
case "tensorflow":
// Create TensorFlow service using existing code
tfService := tfmodel.NewService(cfg, fs, metrics, sema, maxEvaluatorWait)
evaluator := NewTensorFlowEvaluator(tfService)
return evaluator, PlatformTensorFlow, nil

case "triton":
// Create Triton evaluator (stub for now)
evaluator := NewTritonEvaluator(cfg)
return evaluator, PlatformTriton, nil

default:
return nil, "", fmt.Errorf("unsupported platform: %s for model %s", platform, cfg.ID)
}
}

// CreateRouter creates a platform router with the appropriate evaluator
func CreateRouter(cfg *config.Model, fs afs.Service, metrics *gmetric.Service, sema *semaphore.Weighted, maxEvaluatorWait time.Duration) (*Router, error) {
evaluator, platform, err := CreateEvaluator(cfg, fs, metrics, sema, maxEvaluatorWait)
if err != nil {
return nil, err
}

return NewRouter(cfg, evaluator, platform), nil
}
Loading