mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
365 lines
12 KiB
Go
365 lines
12 KiB
Go
/*
|
|
Copyright 2022 The KServe Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package v1alpha1
|
|
|
|
import (
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"knative.dev/pkg/apis"
|
|
duckv1 "knative.dev/pkg/apis/duck/v1"
|
|
)
|
|
|
|
// InferenceGraph is the Schema for the InferenceGraph API for multiple models
|
|
// +k8s:openapi-gen=true
|
|
// +genclient
|
|
// +kubebuilder:object:root=true
|
|
// +kubebuilder:subresource:status
|
|
// +kubebuilder:printcolumn:name="URL",type="string",JSONPath=".status.url"
|
|
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
|
|
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
|
// +kubebuilder:resource:path=inferencegraphs,shortName=ig,singular=inferencegraph
|
|
type InferenceGraph struct {
|
|
metav1.TypeMeta `json:",inline"`
|
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
|
Spec InferenceGraphSpec `json:"spec,omitempty"`
|
|
Status InferenceGraphStatus `json:"status,omitempty"`
|
|
}
|
|
|
|
// InferenceGraphSpec defines the InferenceGraph spec
|
|
// +k8s:openapi-gen=true
|
|
type InferenceGraphSpec struct {
|
|
// Map of InferenceGraph router nodes
|
|
// Each node defines the router which can be different routing types
|
|
Nodes map[string]InferenceRouter `json:"nodes"`
|
|
// +optional
|
|
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
|
// +optional
|
|
Affinity *corev1.Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"`
|
|
// TimeoutSeconds specifies the number of seconds to wait before timing out a request to the component.
|
|
// +optional
|
|
TimeoutSeconds *int64 `json:"timeout,omitempty"`
|
|
// +optional
|
|
RouterTimeouts *InfereceGraphRouterTimeouts `json:"routerTimeouts,omitempty"`
|
|
// Minimum number of replicas, defaults to 1 but can be set to 0 to enable scale-to-zero.
|
|
// +optional
|
|
MinReplicas *int32 `json:"minReplicas,omitempty"`
|
|
// Maximum number of replicas for autoscaling.
|
|
// +optional
|
|
MaxReplicas int32 `json:"maxReplicas,omitempty"`
|
|
// ScaleTarget specifies the integer target value of the metric type the Autoscaler watches for.
|
|
// concurrency and rps targets are supported by Knative Pod Autoscaler
|
|
// (https://knative.dev/docs/serving/autoscaling/autoscaling-targets/).
|
|
// +optional
|
|
ScaleTarget *int32 `json:"scaleTarget,omitempty"`
|
|
// ScaleMetric defines the scaling metric type watched by autoscaler
|
|
// possible values are concurrency, rps, cpu, memory. concurrency, rps are supported via
|
|
// Knative Pod Autoscaler(https://knative.dev/docs/serving/autoscaling/autoscaling-metrics).
|
|
// +optional
|
|
ScaleMetric *ScaleMetric `json:"scaleMetric,omitempty"`
|
|
// Toleration specifies the toleration for the InferenceGraph.
|
|
// https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
|
|
// +optional
|
|
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
|
|
// NodeSelector specifies the node selector for the InferenceGraph.
|
|
// https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
|
|
// +optional
|
|
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
|
// NodeName specifies the node name for the InferenceGraph.
|
|
// https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
|
|
// +optional
|
|
NodeName string `json:"nodeName,omitempty"`
|
|
// ServiceAccountName specifies the service account name for the InferenceGraph.
|
|
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
|
|
// +optional
|
|
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
|
}
|
|
|
|
// ScaleMetric enum
|
|
// +kubebuilder:validation:Enum=cpu;memory;concurrency;rps
|
|
type ScaleMetric string
|
|
|
|
// InferenceRouterType constant for inference routing types
|
|
// +k8s:openapi-gen=true
|
|
// +kubebuilder:validation:Enum=Sequence;Splitter;Ensemble;Switch
|
|
type InferenceRouterType string
|
|
|
|
// InferenceRouterType Enum
|
|
const (
|
|
// Sequence Default type only route to one destination
|
|
Sequence InferenceRouterType = "Sequence"
|
|
|
|
// Splitter router randomly routes the requests to the named service according to the weight
|
|
Splitter InferenceRouterType = "Splitter"
|
|
|
|
// Ensemble router routes the requests to multiple models and then merge the responses
|
|
Ensemble InferenceRouterType = "Ensemble"
|
|
|
|
// Switch routes the request to the model based on certain condition
|
|
Switch InferenceRouterType = "Switch"
|
|
)
|
|
|
|
const (
|
|
// GraphRootNodeName is the root node name.
|
|
GraphRootNodeName string = "root"
|
|
)
|
|
|
|
// +k8s:openapi-gen=true
|
|
type InfereceGraphRouterTimeouts struct {
|
|
// ServerRead specifies the number of seconds to wait before timing out a request read by the server.
|
|
// +optional
|
|
ServerRead *int64 `json:"serverRead,omitempty"`
|
|
// ServerWrite specifies the maximum duration in seconds before timing out writes of the response.
|
|
// +optional
|
|
ServerWrite *int64 `json:"serverWrite,omitempty"`
|
|
// ServerIdle specifies the maximum amount of time in seconds to wait for the next request when keep-alives are enabled.
|
|
// +optional
|
|
ServerIdle *int64 `json:"serverIdle,omitempty"`
|
|
// ServiceClient specifies a time limit in seconds for requests made to the graph components by HTTP client.
|
|
// +optional
|
|
ServiceClient *int64 `json:"serviceClient,omitempty"`
|
|
}
|
|
|
|
// +k8s:openapi-gen=true
|
|
// InferenceRouter defines the router for each InferenceGraph node with one or multiple steps
|
|
//
|
|
// ```yaml
|
|
// kind: InferenceGraph
|
|
// metadata:
|
|
//
|
|
// name: canary-route
|
|
//
|
|
// spec:
|
|
//
|
|
// nodes:
|
|
// root:
|
|
// routerType: Splitter
|
|
// routes:
|
|
// - service: mymodel1
|
|
// weight: 20
|
|
// - service: mymodel2
|
|
// weight: 80
|
|
//
|
|
// ```
|
|
//
|
|
// ```yaml
|
|
// kind: InferenceGraph
|
|
// metadata:
|
|
//
|
|
// name: abtest
|
|
//
|
|
// spec:
|
|
//
|
|
// nodes:
|
|
// mymodel:
|
|
// routerType: Switch
|
|
// routes:
|
|
// - service: mymodel1
|
|
// condition: "{ .input.userId == 1 }"
|
|
// - service: mymodel2
|
|
// condition: "{ .input.userId == 2 }"
|
|
//
|
|
// ```
|
|
//
|
|
// Scoring a case using a model ensemble consists of scoring it using each model separately,
|
|
// then combining the results into a single scoring result using one of the pre-defined combination methods.
|
|
//
|
|
// Tree Ensemble constitutes a case where simple algorithms for combining results of either classification or regression trees are well known.
|
|
// Multiple classification trees, for example, are commonly combined using a "majority-vote" method.
|
|
// Multiple regression trees are often combined using various averaging techniques.
|
|
// e.g tagging models with segment identifiers and weights to be used for their combination in these ways.
|
|
// ```yaml
|
|
// kind: InferenceGraph
|
|
// metadata:
|
|
//
|
|
// name: ensemble
|
|
//
|
|
// spec:
|
|
//
|
|
// nodes:
|
|
// root:
|
|
// routerType: Sequence
|
|
// routes:
|
|
// - service: feast
|
|
// - nodeName: ensembleModel
|
|
// data: $response
|
|
// ensembleModel:
|
|
// routerType: Ensemble
|
|
// routes:
|
|
// - service: sklearn-model
|
|
// - service: xgboost-model
|
|
//
|
|
// ```
|
|
//
|
|
// Scoring a case using a sequence, or chain of models allows the output of one model to be passed in as input to the subsequent models.
|
|
// ```yaml
|
|
// kind: InferenceGraph
|
|
// metadata:
|
|
//
|
|
// name: model-chainer
|
|
//
|
|
// spec:
|
|
//
|
|
// nodes:
|
|
// root:
|
|
// routerType: Sequence
|
|
// routes:
|
|
// - service: mymodel-s1
|
|
// - service: mymodel-s2
|
|
// data: $response
|
|
// - service: mymodel-s3
|
|
// data: $response
|
|
//
|
|
// ```
|
|
//
|
|
// In the flow described below, the pre_processing node base64 encodes the image and passes it to two model nodes in the flow.
|
|
// The encoded data is available to both these nodes for classification. The second node i.e. dog-breed-classification takes the
|
|
// original input from the pre_processing node along-with the response from the cat-dog-classification node to do further classification
|
|
// of the dog breed if required.
|
|
// ```yaml
|
|
// kind: InferenceGraph
|
|
// metadata:
|
|
//
|
|
// name: dog-breed-classification
|
|
//
|
|
// spec:
|
|
//
|
|
// nodes:
|
|
// root:
|
|
// routerType: Sequence
|
|
// routes:
|
|
// - service: cat-dog-classifier
|
|
// - nodeName: breed-classifier
|
|
// data: $request
|
|
// breed-classifier:
|
|
// routerType: Switch
|
|
// routes:
|
|
// - service: dog-breed-classifier
|
|
// condition: { .predictions.class == "dog" }
|
|
// - service: cat-breed-classifier
|
|
// condition: { .predictions.class == "cat" }
|
|
//
|
|
// ```
|
|
type InferenceRouter struct {
|
|
// RouterType
|
|
//
|
|
// - `Sequence:` chain multiple inference steps with input/output from previous step
|
|
//
|
|
// - `Splitter:` randomly routes to the target service according to the weight
|
|
//
|
|
// - `Ensemble:` routes the request to multiple models and then merge the responses
|
|
//
|
|
// - `Switch:` routes the request to one of the steps based on condition
|
|
//
|
|
RouterType InferenceRouterType `json:"routerType"`
|
|
|
|
// Steps defines destinations for the current router node
|
|
// +optional
|
|
Steps []InferenceStep `json:"steps,omitempty"`
|
|
}
|
|
|
|
// +k8s:openapi-gen=true
|
|
// Exactly one InferenceTarget field must be specified
|
|
type InferenceTarget struct {
|
|
// The node name for routing as next step
|
|
// +optional
|
|
NodeName string `json:"nodeName,omitempty"`
|
|
|
|
// named reference for InferenceService
|
|
ServiceName string `json:"serviceName,omitempty"`
|
|
|
|
// InferenceService URL, mutually exclusive with ServiceName
|
|
// +optional
|
|
ServiceURL string `json:"serviceUrl,omitempty"`
|
|
}
|
|
|
|
// InferenceStepDependencyType constant for inference step dependency
|
|
// +k8s:openapi-gen=true
|
|
// +kubebuilder:validation:Enum=Soft;Hard
|
|
type InferenceStepDependencyType string
|
|
|
|
// StepDependency Enum
|
|
const (
|
|
// Soft
|
|
Soft InferenceStepDependencyType = "Soft"
|
|
|
|
// Hard
|
|
Hard InferenceStepDependencyType = "Hard"
|
|
)
|
|
|
|
// InferenceStep defines the inference target of the current step with condition, weights and data.
|
|
// +k8s:openapi-gen=true
|
|
type InferenceStep struct {
|
|
// Unique name for the step within this node
|
|
// +optional
|
|
StepName string `json:"name,omitempty"`
|
|
|
|
// Node or service used to process this step
|
|
InferenceTarget `json:",inline"`
|
|
|
|
// request data sent to the next route with input/output from the previous step
|
|
// $request
|
|
// $response.predictions
|
|
// +optional
|
|
Data string `json:"data,omitempty"`
|
|
|
|
// If true, maps the 'predictions' field from the previous step's response
|
|
// to the 'instances' field of this step's request.
|
|
// Useful in sequential inference graphs where one step's output
|
|
// becomes the input for the next.
|
|
// +optional
|
|
MapPredictionsToInstances bool `json:"mapPredictionsToInstances,omitempty"`
|
|
|
|
// the weight for split of the traffic, only used for Split Router
|
|
// when weight is specified all the routing targets should be sum to 100
|
|
// +optional
|
|
Weight *int64 `json:"weight,omitempty"`
|
|
|
|
// routing based on the condition
|
|
// +optional
|
|
Condition string `json:"condition,omitempty"`
|
|
|
|
// to decide whether a step is a hard or a soft dependency in the Inference Graph
|
|
// +optional
|
|
Dependency InferenceStepDependencyType `json:"dependency,omitempty"`
|
|
}
|
|
|
|
// InferenceGraphStatus defines the InferenceGraph conditions and status
|
|
// +k8s:openapi-gen=true
|
|
type InferenceGraphStatus struct {
|
|
// Conditions for InferenceGraph
|
|
duckv1.Status `json:",inline"`
|
|
// Url for the InferenceGraph
|
|
// +optional
|
|
URL *apis.URL `json:"url,omitempty"`
|
|
// InferenceGraph DeploymentMode
|
|
DeploymentMode string `json:"deploymentMode,omitempty"`
|
|
}
|
|
|
|
// InferenceGraphList contains a list of InferenceGraph
|
|
// +k8s:openapi-gen=true
|
|
// +kubebuilder:object:root=true
|
|
type InferenceGraphList struct {
|
|
metav1.TypeMeta `json:",inline"`
|
|
metav1.ListMeta `json:"metadata,omitempty"`
|
|
// +listType=set
|
|
Items []InferenceGraph `json:"items"`
|
|
}
|
|
|
|
func init() {
|
|
SchemeBuilder.Register(&InferenceGraph{}, &InferenceGraphList{})
|
|
}
|