diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..9fff87d18f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*.swp +bin diff --git a/README.md b/README.md index 4efc6145cb4..d1c43de82a9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,377 @@ -# hp-tuning -Repository for hyperparameter tuning +# Katib +HyperParamete Tuning on Kubernetes. +This project is [Google vizier](https://static.googleusercontent.com/media/research.google.com/ja//pubs/archive/bcb15507f4b52991a0783013df4222240e942381.pdf) inspired. +Katib is a scalable and flexible hyperparameter tuning framework and tightly integrate with kubernetes. +And it does not depend on a specific DL framework. +There are examples of three frameworks ( tensorflow, mxnet, and pytorch). +## Name +Katib stands for `secretary` in Arabic. +As Vizier stands for high official or prime minister in Arabic, I named this project Katib in honor of Vizier. + +## Vizier compatible +Katib has Study, Trial and Suggestion that are defined in Goodle vizier. + +### Study +Represents a single optimization run over a feasible space. +Each Study contains a configuration describing the feasible space, as well as a set of Trials. +It is assumed that objective function f(x) does not change in the course of a Study. + +### Trial +A list of parameter values, x, that will lead to a single evaluation of f(x). +A trial can be “Completed”, which means that it has been evaluated and the objective value f(x) has been assigned to it, otherwise it is “Pending”. +One trial corresponding to one k8s Job. + +### Suggestion +An algorithm to make parameter set. +Currently parameter expolalation algorithms Katib supported are + +* random +* grid +* [hyperband](https://arxiv.org/pdf/1603.06560.pdf) + +## Components +Katib consists of several components as below. +Each component is running on k8s as a deployment. +And each component communicates with grpc, the API is defined at `API/api.proto`. + +- vizier: main components. + - vizier-core : API server of vizier. + - vizier-db +- dlk-manager : a interface of kubernetes. +- suggesiont : implimentations of each expolalation algorithm. + - vizier-suggestion-random + - vizier-suggestion-grid + - vizier-suggestion-hyperband +- modeldb : WebUI + - modeldb-frontend + - modeldb-backend + - modeldb-db + +## StudyConfig +In Study config file, you define the feasible space of parameters and configuration of kubernetes job. +Examples of Study config are in `conf` directory. +The configuration items are as follows. + +- name: Study name +- owner: Owner +- objectivevaluename: Name of the objective value. Your evaluated software should be print log `{objectivevaluename}={objective value}` in std-io. +- optimizationtype: Optimization direction of the objective value. 1=maximize 2=minimize +- suggestalgorithm: [random, grid, hyperband] now +- suggestionparameters: Parameter of the algorithm. Set name-value style. + - In random suggestion + - SuggestionNum: How many suggestions will Katib create. + - MaxParallel: Max number of run on kubernetes + - In grid suggestion + - MaxParallel: Max number of run on kubernetes + - GridDefault: default number of grid + - name: [parameter name] grid number of specified parameter. +- metrics: The value you want to save to modeldb besides objectivevaluename. +- image: docker image name +- mount + - pvc: pvc + - path: MountPath in container +- pullsecret: Name of Image pull secret +- gpu: number of GPU (If you want to run cpu task, set 0 or delete this parameter) +- command: commands +- parameterconfigs: define feasible space + - configs + - name : parameter space + - parametertype: 1=float, 2=int, 4=categorical + - feasible + - min + - max + - list (for categorical) + +## Web UI +Katib provide Web UI based on ModelDB( https://github.com/mitdbg/modeldb ). +The ingress setting is defined in manifests/modeldb/frontend/ingress.yaml + +## Getting Start + +### Requirements +- docker +- kubernetes cluster ( kubectlable from your PC and if you want to use GPU, set up k8s [GPU scheduling]( https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/ )) +- Ingress controller (e.g. nginx) + +### Install +First, Copy CLI tool. + +``` +docker pull katib/katib-cli +docker run --name katib-cli -itd katib/katib-cli sh +docker cp katib-cli:/go/src/github.com/mlkube/katib/cli/katib-cli bin/katib-cli +docker rm -f katib-cli +``` +The cli tool will be put `bin` directory. + +Let's deploy Katib on your cluster. +Kubernetes manifests are in `manifests` directory. +Set the environment of your cluster(Ingress, Persistent Volumes). + +``` +$ ./deploy +``` +### Use CLI +Check which node the vizier-core was deployed. +Then access vizier API. +``` +$ kubectl get -n katib pod -o wide +NAME READY STATUS RESTARTS AGE IP NODE +dlk-manager-6d8886f988-m485v 1/1 Running 0 11m 10.44.0.4 node2 +modeldb-backend-57667f44f6-5cl8k 1/1 Running 0 11m 10.35.0.4 gpu-node2 +modeldb-db-6fc46458f6-fv2mn 1/1 Running 0 11m 10.47.0.4 gpu-node3 +modeldb-frontend-5f6cf5c496-m7gxc 1/1 Running 0 11m 10.39.0.4 gpu-node1 +vizier-core-864dd6fdd4-r55qv 1/1 Running 0 11m 10.35.0.5 gpu-node2 +vizier-db-7b6f8c59bc-mjhh4 1/1 Running 0 11m 10.36.0.4 node1 +vizier-suggestion-random-5895dc79b4-pbqkc 1/1 Running 0 11m 10.47.0.5 gpu-node3 + +$ ./katib-cli -s gpu-node2:30678 Getstudies +2018/04/03 05:14:49 connecting gpu-node2:30678 +StudyID Name Owner RunningTrial CompletedTrial +``` +### Create Example Study +Try Createstudy. Study will be created and start hyperparameter search. + +``` +$ ./katib-cli -s gpu-node2:30678 -f ../conf/random.yml Createstudy +2018/04/03 05:16:37 connecting gpu-node2:30678 +2018/04/03 05:16:37 study conf{cifer10 root MAXIMIZE 0 configs: > configs: > configs: > configs: > configs: > [] random median [name:"SuggestionNum" value:"2" name:"MaxParallel" value:"2" ] [] Validation-accuracy [accuracy] mxnet/python:gpu [python /mxnet/example/image-classification/train_cifar10.py --batch-size=512 --gpus=0,1] 2 default-scheduler } +2018/04/03 05:16:37 req Createstudy +2018/04/03 05:16:37 CreateStudy: study_id:"fef3711aa343fae6" +``` + +You can check the job is running with `kubectl` command. + +``` +$ ./katib-cli -s gpu-node2:30678 Getstudies +2018/04/03 05:19:49 connecting gpu-node2:30678 +StudyID Name Owner RunningTrial CompletedTrial +fef3711aa343fae6 cifer10 root 2 0 + +$ kubectl get -n katib job +NAME DESIRED SUCCESSFUL AGE +b325ec8d96ce16df-worker-0 1 0 1m +wbe8aabd6ad4f50e-worker-0 1 0 1m +``` + +Check the status of jobs with `katib-cli` command. + +``` +$ ./katib-cli -s gpu-node2:30678 Getstudies +2018/04/03 05:26:20 connecting gpu-node2:30678 +StudyID Name Owner RunningTrial CompletedTrial +fef3711aa343fae6 cifer10 root 1 1 +``` + +When some trials are completed, you can check the result of completed trials. +See endpoint of Katib UI ingress. +In this example, the endpoint is `k-cluster.example.net/katib` + +``` +$ kubectl -n katib describe ingress katib-ui +Name: katib-ui +Namespace: katib +Address: +Default backend: default-http-backend:80 () +Rules: + Host Path Backends + ---- ---- -------- + k-cluster.example.net + /katib modeldb-frontend:3000 () +Annotations: +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal CREATE 1m nginx-ingress-controller Ingress katib/katib-ui + Normal UPDATE 1m nginx-ingress-controller Ingress katib/katib-ui +``` + +### Use Persistent Volume +Create PV and PVC in katib namespace. +For example, + +`pv_nfs.yml` + +``` +#PV manifest +apiVersion: v1 +kind: PersistentVolume +metadata: + name: nfs + namespace: katib + labels: + type: "nfs" +spec: + capacity: + storage: 300Gi + accessModes: + - ReadWriteMany + nfs: + server: 192.168.1.3 + path: "/nfs/" +``` +`pvc_nfs.yml` +``` +#PVC manifest +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: nfs + namespace: katib +spec: + accessModes: + - ReadWriteMany + storageClassName: "" + resources: + requests: + storage: 300Gi + selector: + matchLabels: + type: "nfs" +``` + +``` +$ kubectl apply -f pv_nfs.yml +persistentvolume "nfs" created + +$ kubectl apply -f pvc_nfs.yml +persistentvolumeclaim "nfs" created +``` + +Then set up mount config in StudyConfig like below. + +``` +name: cifer10 +owner: root +optimizationtype: 2 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: Validation-accuracy +scheduler: default-scheduler +image: mxnet/python:gpu +mount: + pvc: nfs + path: /nfs-mnt +gpu: 1 +suggestionparameters: + - + name: SuggestionNum + value: 2 + - + name: MaxParallel + value: 2 +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 + - --gpus=0 + - --num-epochs=3 +metrics: + - accuracy +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 + - + name: --max-random-l + parametertype: 2 + feasible: + min: 25 + max: 75 +``` + +``` +$ ./katib-cli -s gpu-node2:30678 -f ../conf/random-pv.yml Createstudy +2018/04/03 05:49:47 connecting gpu-node2:30678 +2018/04/03 05:49:47 study conf{cifer10-pv-test root MAXIMIZE 0 configs: > configs: > configs: > configs: > configs: > [] random median [name:"SuggestionNum" value:"2" name:"MaxParallel" value:"2" ] [] Validation-accuracy [accuracy] mxnet/python:gpu [python /mxnet/example/image-classification/train_cifar10.py --batch-size=512 --gpus=0,1] 2 default-scheduler pvc:"nfs" path:"/nfs-mnt" } +2018/04/03 05:49:47 req Createstudy +2018/04/03 05:49:47 CreateStudy: study_id:"p6ee7933f2b62f30" +``` +Now the jobs will use the input files in the nfs. + + +## TensorBoard Integration +Not only TensorFlow but also several DL flameworks (e.g. PyTorch, MxNet) support TnsorBoard format logging. +Katib can integrate TensorBoard easily. +To use TensorBoard from Katib, you should define persistent volume clame and set mount config for the Study. +Katib search each trial log in `{pvc mount path}/logs/{Study ID}/{Trial ID}`. +`{{STUDY_ID}}` and `{{TRIAL_ID}}` in the Studyconfig file are replaced the corresponding value when creating each job. +See example `conf/tf-nmt.yml` that is a config for parameter tuning of [tensorflow/nmt](https://github.com/tensorflow/nmt). + +``` +./katib-cli -s gpu-node2:30678 -f ../conf/tf-nmt.yml Createstudy +2018/04/03 05:52:11 connecting gpu-node2:30678 +2018/04/03 05:52:11 study conf{tf-nmt root MINIMIZE 0 configs: > configs: > configs: > configs: > configs: > configs: > configs: > [] random median [name:"SuggestionNum" value:"10" name:"MaxParallel" value:"6" ] [] test_ppl [ppl bleu_dev bleu_test] yujioshima/tf-nmt:latest-gpu [python -m nmt.nmt --src=vi --tgt=en --out_dir=/nfs-mnt/logs/{{STUDY_ID}}_{{TRIAL_ID}} --vocab_prefix=/nfs-mnt/learndatas/wmt15_en_vi/vocab --train_prefix=/nfs-mnt/learndatas/wmt15_en_vi/train --dev_prefix=/nfs-mnt/learndatas/wmt15_en_vi/tst2012 --test_prefix=/nfs-mnt/learndatas/wmt15_en_vi/tst2013 --attention_architecture=standard --attention=normed_bahdanau --batch_size=128 --colocate_gradients_with_ops=true --eos= --forget_bias=1.0 --init_weight=0.1 --learning_rate=1.0 --max_gradient_norm=5.0 --metrics=bleu --share_vocab=false --num_buckets=5 --optimizer=sgd --sos= --steps_per_stats=100 --time_major=true --unit_type=lstm --src_max_len=50 --tgt_max_len=50 --infer_batch_size=32] 1 default-scheduler pvc:"nfs" path:"/nfs-mnt" } +2018/04/03 05:52:11 req Createstudy +2018/04/03 05:52:11 CreateStudy: study_id:"n5c80f4af709a70d" +``` +Make TensorBord deployments, services, and ingress automatically and you can access from Web UI. + +![katib-demo](https://user-images.githubusercontent.com/10014831/38241910-64fb0646-376e-11e8-8b98-c26e577f3935.gif) + + +## CLI +### katib +##### options +- s +Set address of vizier-core. {IP Addr}:{Port}. default localhost:6789 +Katib API is grpc. +Unfortunately, nginx ingress controller does not support grpc now ( next version it will support! ) +So vizier-core expose port as NodePort(30678}. + +#### Getstudys +Get list of studys and their status. + +#### Createstudy +Send create new study request to katib api server. + +##### options +- f +Specify the config file of your study. + +### Stopstudy [Study_ID] +Delete specified study from API server. +But the results of trials in modelDB won't be deleted. + +## Implement new suggestion algorithm +Suggestion API is defined as grpc service at `API/api.proto`. +You can attach new algorithm easily. + +- implement suggestion API +- make k8s service named vizier-suggestion-{ algorithm-name } and expose port 6789 + +And to add new suggestion service, you don't need to stop components ( vizier-core, modeldb, and anything) that are already running. + +## Build from source +You can build all images from source. +``` +./build +``` + +## Uninstall +Delete `katib` namespace from your kubernetes cluster. +All components will be deleted +``` +kubectl delete ns katib +``` + +## TODOs +* Integrate KubeFlow (tf/pytorch/caffe2/-operator) +* Support Early Stopping +* Enrich the GUI diff --git a/api/Makefile b/api/Makefile new file mode 100644 index 00000000000..f63e1d5a8f4 --- /dev/null +++ b/api/Makefile @@ -0,0 +1,2 @@ +api.pb.go: api.proto + protoc -I. api.proto --go_out=plugins=grpc:. diff --git a/api/api.pb.go b/api/api.pb.go new file mode 100644 index 00000000000..6bf2c89763d --- /dev/null +++ b/api/api.pb.go @@ -0,0 +1,1768 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: api.proto + +/* +Package api is a generated protocol buffer package. + +It is generated from these files: + api.proto + +It has these top-level messages: + FeasibleSpace + ParameterConfig + Parameter + Metrics + EvaluationLog + SuggestionParameter + Tag + MountConf + Trial + StudyConfig + CreateStudyRequest + CreateStudyReply + StopStudyRequest + StopStudyReply + GetStudysRequest + StudyInfo + GetStudysReply + SuggestTrialsRequest + SuggestTrialsReply + CompleteTrialRequest + CompleteTrialReply + ShouldTrialStopRequest + ShouldTrialStopReply + GetObjectValueRequest + GetObjectValueReply + AddMeasurementToTrialsRequest + AddMeasurementToTrialsReply + InitializeSuggestServiceRequest + InitializeSuggestServiceReply + GenerateTrialsRequest + GenerateTrialsReply + SetSuggestionParametersRequest + SetSuggestionParametersReply + StopSuggestionRequest + StopSuggestionReply +*/ +package api + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type ParameterType int32 + +const ( + // Not used + ParameterType_UNKNOWN_TYPE ParameterType = 0 + ParameterType_DOUBLE ParameterType = 1 + ParameterType_INT ParameterType = 2 + ParameterType_DISCRETE ParameterType = 3 + ParameterType_CATEGORICAL ParameterType = 4 +) + +var ParameterType_name = map[int32]string{ + 0: "UNKNOWN_TYPE", + 1: "DOUBLE", + 2: "INT", + 3: "DISCRETE", + 4: "CATEGORICAL", +} +var ParameterType_value = map[string]int32{ + "UNKNOWN_TYPE": 0, + "DOUBLE": 1, + "INT": 2, + "DISCRETE": 3, + "CATEGORICAL": 4, +} + +func (x ParameterType) String() string { + return proto.EnumName(ParameterType_name, int32(x)) +} +func (ParameterType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type OptimizationType int32 + +const ( + // Not used + OptimizationType_UNKNOWN_OPTIMIZATION OptimizationType = 0 + OptimizationType_MINIMIZE OptimizationType = 1 + OptimizationType_MAXIMIZE OptimizationType = 2 +) + +var OptimizationType_name = map[int32]string{ + 0: "UNKNOWN_OPTIMIZATION", + 1: "MINIMIZE", + 2: "MAXIMIZE", +} +var OptimizationType_value = map[string]int32{ + "UNKNOWN_OPTIMIZATION": 0, + "MINIMIZE": 1, + "MAXIMIZE": 2, +} + +func (x OptimizationType) String() string { + return proto.EnumName(OptimizationType_name, int32(x)) +} +func (OptimizationType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +// This value is stored as TINYINT in MySQL. +type TrialState int32 + +const ( + TrialState_PENDING TrialState = 0 + TrialState_RUNNING TrialState = 1 + TrialState_COMPLETED TrialState = 2 + TrialState_KILLED TrialState = 3 + TrialState_ERROR TrialState = 120 +) + +var TrialState_name = map[int32]string{ + 0: "PENDING", + 1: "RUNNING", + 2: "COMPLETED", + 3: "KILLED", + 120: "ERROR", +} +var TrialState_value = map[string]int32{ + "PENDING": 0, + "RUNNING": 1, + "COMPLETED": 2, + "KILLED": 3, + "ERROR": 120, +} + +func (x TrialState) String() string { + return proto.EnumName(TrialState_name, int32(x)) +} +func (TrialState) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +type FeasibleSpace struct { + Max string `protobuf:"bytes,1,opt,name=max" json:"max,omitempty"` + Min string `protobuf:"bytes,2,opt,name=min" json:"min,omitempty"` + List []string `protobuf:"bytes,3,rep,name=list" json:"list,omitempty"` +} + +func (m *FeasibleSpace) Reset() { *m = FeasibleSpace{} } +func (m *FeasibleSpace) String() string { return proto.CompactTextString(m) } +func (*FeasibleSpace) ProtoMessage() {} +func (*FeasibleSpace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *FeasibleSpace) GetMax() string { + if m != nil { + return m.Max + } + return "" +} + +func (m *FeasibleSpace) GetMin() string { + if m != nil { + return m.Min + } + return "" +} + +func (m *FeasibleSpace) GetList() []string { + if m != nil { + return m.List + } + return nil +} + +type ParameterConfig struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + ParameterType ParameterType `protobuf:"varint,2,opt,name=parameter_type,json=parameterType,enum=api.ParameterType" json:"parameter_type,omitempty"` + // The following values defines a feasible parameter space. + Feasible *FeasibleSpace `protobuf:"bytes,3,opt,name=feasible" json:"feasible,omitempty"` +} + +func (m *ParameterConfig) Reset() { *m = ParameterConfig{} } +func (m *ParameterConfig) String() string { return proto.CompactTextString(m) } +func (*ParameterConfig) ProtoMessage() {} +func (*ParameterConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *ParameterConfig) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *ParameterConfig) GetParameterType() ParameterType { + if m != nil { + return m.ParameterType + } + return ParameterType_UNKNOWN_TYPE +} + +func (m *ParameterConfig) GetFeasible() *FeasibleSpace { + if m != nil { + return m.Feasible + } + return nil +} + +type Parameter struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + ParameterType ParameterType `protobuf:"varint,2,opt,name=parameter_type,json=parameterType,enum=api.ParameterType" json:"parameter_type,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` +} + +func (m *Parameter) Reset() { *m = Parameter{} } +func (m *Parameter) String() string { return proto.CompactTextString(m) } +func (*Parameter) ProtoMessage() {} +func (*Parameter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *Parameter) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Parameter) GetParameterType() ParameterType { + if m != nil { + return m.ParameterType + } + return ParameterType_UNKNOWN_TYPE +} + +func (m *Parameter) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type Metrics struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` +} + +func (m *Metrics) Reset() { *m = Metrics{} } +func (m *Metrics) String() string { return proto.CompactTextString(m) } +func (*Metrics) ProtoMessage() {} +func (*Metrics) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *Metrics) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Metrics) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type EvaluationLog struct { + Time string `protobuf:"bytes,1,opt,name=time" json:"time,omitempty"` + Metrics []*Metrics `protobuf:"bytes,2,rep,name=metrics" json:"metrics,omitempty"` +} + +func (m *EvaluationLog) Reset() { *m = EvaluationLog{} } +func (m *EvaluationLog) String() string { return proto.CompactTextString(m) } +func (*EvaluationLog) ProtoMessage() {} +func (*EvaluationLog) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *EvaluationLog) GetTime() string { + if m != nil { + return m.Time + } + return "" +} + +func (m *EvaluationLog) GetMetrics() []*Metrics { + if m != nil { + return m.Metrics + } + return nil +} + +type SuggestionParameter struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` +} + +func (m *SuggestionParameter) Reset() { *m = SuggestionParameter{} } +func (m *SuggestionParameter) String() string { return proto.CompactTextString(m) } +func (*SuggestionParameter) ProtoMessage() {} +func (*SuggestionParameter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *SuggestionParameter) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *SuggestionParameter) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type Tag struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` +} + +func (m *Tag) Reset() { *m = Tag{} } +func (m *Tag) String() string { return proto.CompactTextString(m) } +func (*Tag) ProtoMessage() {} +func (*Tag) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *Tag) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Tag) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type MountConf struct { + Pvc string `protobuf:"bytes,1,opt,name=pvc" json:"pvc,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"` +} + +func (m *MountConf) Reset() { *m = MountConf{} } +func (m *MountConf) String() string { return proto.CompactTextString(m) } +func (*MountConf) ProtoMessage() {} +func (*MountConf) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *MountConf) GetPvc() string { + if m != nil { + return m.Pvc + } + return "" +} + +func (m *MountConf) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +type Trial struct { + TrialId string `protobuf:"bytes,1,opt,name=trial_id,json=trialId" json:"trial_id,omitempty"` + StudyId string `protobuf:"bytes,2,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + ParameterSet []*Parameter `protobuf:"bytes,3,rep,name=parameter_set,json=parameterSet" json:"parameter_set,omitempty"` + Status TrialState `protobuf:"varint,4,opt,name=status,enum=api.TrialState" json:"status,omitempty"` + EvalLogs []*EvaluationLog `protobuf:"bytes,5,rep,name=eval_logs,json=evalLogs" json:"eval_logs,omitempty"` + ObjectiveValue string `protobuf:"bytes,6,opt,name=objective_value,json=objectiveValue" json:"objective_value,omitempty"` + Tags []*Tag `protobuf:"bytes,7,rep,name=tags" json:"tags,omitempty"` +} + +func (m *Trial) Reset() { *m = Trial{} } +func (m *Trial) String() string { return proto.CompactTextString(m) } +func (*Trial) ProtoMessage() {} +func (*Trial) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *Trial) GetTrialId() string { + if m != nil { + return m.TrialId + } + return "" +} + +func (m *Trial) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *Trial) GetParameterSet() []*Parameter { + if m != nil { + return m.ParameterSet + } + return nil +} + +func (m *Trial) GetStatus() TrialState { + if m != nil { + return m.Status + } + return TrialState_PENDING +} + +func (m *Trial) GetEvalLogs() []*EvaluationLog { + if m != nil { + return m.EvalLogs + } + return nil +} + +func (m *Trial) GetObjectiveValue() string { + if m != nil { + return m.ObjectiveValue + } + return "" +} + +func (m *Trial) GetTags() []*Tag { + if m != nil { + return m.Tags + } + return nil +} + +type StudyConfig struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Owner string `protobuf:"bytes,2,opt,name=owner" json:"owner,omitempty"` + OptimizationType OptimizationType `protobuf:"varint,3,opt,name=optimization_type,json=optimizationType,enum=api.OptimizationType" json:"optimization_type,omitempty"` + OptimizationGoal float64 `protobuf:"fixed64,4,opt,name=optimization_goal,json=optimizationGoal" json:"optimization_goal,omitempty"` + ParameterConfigs *StudyConfig_ParameterConfigs `protobuf:"bytes,5,opt,name=parameter_configs,json=parameterConfigs" json:"parameter_configs,omitempty"` + AccessPermissions []string `protobuf:"bytes,6,rep,name=access_permissions,json=accessPermissions" json:"access_permissions,omitempty"` + SuggestAlgorithm string `protobuf:"bytes,7,opt,name=suggest_algorithm,json=suggestAlgorithm" json:"suggest_algorithm,omitempty"` + AutostopAlgorithm string `protobuf:"bytes,8,opt,name=autostop_algorithm,json=autostopAlgorithm" json:"autostop_algorithm,omitempty"` + StudyTaskName string `protobuf:"bytes,9,opt,name=study_task_name,json=studyTaskName" json:"study_task_name,omitempty"` + SuggestionParameters []*SuggestionParameter `protobuf:"bytes,10,rep,name=suggestion_parameters,json=suggestionParameters" json:"suggestion_parameters,omitempty"` + Tags []*Tag `protobuf:"bytes,11,rep,name=tags" json:"tags,omitempty"` + ObjectiveValueName string `protobuf:"bytes,12,opt,name=objective_value_name,json=objectiveValueName" json:"objective_value_name,omitempty"` + Metrics []string `protobuf:"bytes,13,rep,name=metrics" json:"metrics,omitempty"` + Image string `protobuf:"bytes,14,opt,name=image" json:"image,omitempty"` + Command []string `protobuf:"bytes,15,rep,name=command" json:"command,omitempty"` + Gpu int32 `protobuf:"varint,16,opt,name=gpu" json:"gpu,omitempty"` + Scheduler string `protobuf:"bytes,17,opt,name=scheduler" json:"scheduler,omitempty"` + Mount *MountConf `protobuf:"bytes,18,opt,name=mount" json:"mount,omitempty"` + PullSecret string `protobuf:"bytes,19,opt,name=pull_secret,json=pullSecret" json:"pull_secret,omitempty"` +} + +func (m *StudyConfig) Reset() { *m = StudyConfig{} } +func (m *StudyConfig) String() string { return proto.CompactTextString(m) } +func (*StudyConfig) ProtoMessage() {} +func (*StudyConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *StudyConfig) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *StudyConfig) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *StudyConfig) GetOptimizationType() OptimizationType { + if m != nil { + return m.OptimizationType + } + return OptimizationType_UNKNOWN_OPTIMIZATION +} + +func (m *StudyConfig) GetOptimizationGoal() float64 { + if m != nil { + return m.OptimizationGoal + } + return 0 +} + +func (m *StudyConfig) GetParameterConfigs() *StudyConfig_ParameterConfigs { + if m != nil { + return m.ParameterConfigs + } + return nil +} + +func (m *StudyConfig) GetAccessPermissions() []string { + if m != nil { + return m.AccessPermissions + } + return nil +} + +func (m *StudyConfig) GetSuggestAlgorithm() string { + if m != nil { + return m.SuggestAlgorithm + } + return "" +} + +func (m *StudyConfig) GetAutostopAlgorithm() string { + if m != nil { + return m.AutostopAlgorithm + } + return "" +} + +func (m *StudyConfig) GetStudyTaskName() string { + if m != nil { + return m.StudyTaskName + } + return "" +} + +func (m *StudyConfig) GetSuggestionParameters() []*SuggestionParameter { + if m != nil { + return m.SuggestionParameters + } + return nil +} + +func (m *StudyConfig) GetTags() []*Tag { + if m != nil { + return m.Tags + } + return nil +} + +func (m *StudyConfig) GetObjectiveValueName() string { + if m != nil { + return m.ObjectiveValueName + } + return "" +} + +func (m *StudyConfig) GetMetrics() []string { + if m != nil { + return m.Metrics + } + return nil +} + +func (m *StudyConfig) GetImage() string { + if m != nil { + return m.Image + } + return "" +} + +func (m *StudyConfig) GetCommand() []string { + if m != nil { + return m.Command + } + return nil +} + +func (m *StudyConfig) GetGpu() int32 { + if m != nil { + return m.Gpu + } + return 0 +} + +func (m *StudyConfig) GetScheduler() string { + if m != nil { + return m.Scheduler + } + return "" +} + +func (m *StudyConfig) GetMount() *MountConf { + if m != nil { + return m.Mount + } + return nil +} + +func (m *StudyConfig) GetPullSecret() string { + if m != nil { + return m.PullSecret + } + return "" +} + +type StudyConfig_ParameterConfigs struct { + Configs []*ParameterConfig `protobuf:"bytes,1,rep,name=configs" json:"configs,omitempty"` +} + +func (m *StudyConfig_ParameterConfigs) Reset() { *m = StudyConfig_ParameterConfigs{} } +func (m *StudyConfig_ParameterConfigs) String() string { return proto.CompactTextString(m) } +func (*StudyConfig_ParameterConfigs) ProtoMessage() {} +func (*StudyConfig_ParameterConfigs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9, 0} } + +func (m *StudyConfig_ParameterConfigs) GetConfigs() []*ParameterConfig { + if m != nil { + return m.Configs + } + return nil +} + +type CreateStudyRequest struct { + StudyConfig *StudyConfig `protobuf:"bytes,1,opt,name=study_config,json=studyConfig" json:"study_config,omitempty"` +} + +func (m *CreateStudyRequest) Reset() { *m = CreateStudyRequest{} } +func (m *CreateStudyRequest) String() string { return proto.CompactTextString(m) } +func (*CreateStudyRequest) ProtoMessage() {} +func (*CreateStudyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *CreateStudyRequest) GetStudyConfig() *StudyConfig { + if m != nil { + return m.StudyConfig + } + return nil +} + +type CreateStudyReply struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` +} + +func (m *CreateStudyReply) Reset() { *m = CreateStudyReply{} } +func (m *CreateStudyReply) String() string { return proto.CompactTextString(m) } +func (*CreateStudyReply) ProtoMessage() {} +func (*CreateStudyReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *CreateStudyReply) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +type StopStudyRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` +} + +func (m *StopStudyRequest) Reset() { *m = StopStudyRequest{} } +func (m *StopStudyRequest) String() string { return proto.CompactTextString(m) } +func (*StopStudyRequest) ProtoMessage() {} +func (*StopStudyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *StopStudyRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +type StopStudyReply struct { +} + +func (m *StopStudyReply) Reset() { *m = StopStudyReply{} } +func (m *StopStudyReply) String() string { return proto.CompactTextString(m) } +func (*StopStudyReply) ProtoMessage() {} +func (*StopStudyReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +type GetStudysRequest struct { +} + +func (m *GetStudysRequest) Reset() { *m = GetStudysRequest{} } +func (m *GetStudysRequest) String() string { return proto.CompactTextString(m) } +func (*GetStudysRequest) ProtoMessage() {} +func (*GetStudysRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +type StudyInfo struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Owner string `protobuf:"bytes,3,opt,name=owner" json:"owner,omitempty"` + RunningTrialNum int32 `protobuf:"varint,4,opt,name=running_trial_num,json=runningTrialNum" json:"running_trial_num,omitempty"` + CompletedTrialNum int32 `protobuf:"varint,5,opt,name=completed_trial_num,json=completedTrialNum" json:"completed_trial_num,omitempty"` +} + +func (m *StudyInfo) Reset() { *m = StudyInfo{} } +func (m *StudyInfo) String() string { return proto.CompactTextString(m) } +func (*StudyInfo) ProtoMessage() {} +func (*StudyInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } + +func (m *StudyInfo) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *StudyInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *StudyInfo) GetOwner() string { + if m != nil { + return m.Owner + } + return "" +} + +func (m *StudyInfo) GetRunningTrialNum() int32 { + if m != nil { + return m.RunningTrialNum + } + return 0 +} + +func (m *StudyInfo) GetCompletedTrialNum() int32 { + if m != nil { + return m.CompletedTrialNum + } + return 0 +} + +type GetStudysReply struct { + StudyInfos []*StudyInfo `protobuf:"bytes,1,rep,name=study_infos,json=studyInfos" json:"study_infos,omitempty"` +} + +func (m *GetStudysReply) Reset() { *m = GetStudysReply{} } +func (m *GetStudysReply) String() string { return proto.CompactTextString(m) } +func (*GetStudysReply) ProtoMessage() {} +func (*GetStudysReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } + +func (m *GetStudysReply) GetStudyInfos() []*StudyInfo { + if m != nil { + return m.StudyInfos + } + return nil +} + +type SuggestTrialsRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + SuggestAlgorithm string `protobuf:"bytes,2,opt,name=suggest_algorithm,json=suggestAlgorithm" json:"suggest_algorithm,omitempty"` + Configs *StudyConfig `protobuf:"bytes,3,opt,name=configs" json:"configs,omitempty"` +} + +func (m *SuggestTrialsRequest) Reset() { *m = SuggestTrialsRequest{} } +func (m *SuggestTrialsRequest) String() string { return proto.CompactTextString(m) } +func (*SuggestTrialsRequest) ProtoMessage() {} +func (*SuggestTrialsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } + +func (m *SuggestTrialsRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *SuggestTrialsRequest) GetSuggestAlgorithm() string { + if m != nil { + return m.SuggestAlgorithm + } + return "" +} + +func (m *SuggestTrialsRequest) GetConfigs() *StudyConfig { + if m != nil { + return m.Configs + } + return nil +} + +type SuggestTrialsReply struct { + Trials []*Trial `protobuf:"bytes,1,rep,name=trials" json:"trials,omitempty"` + Completed bool `protobuf:"varint,2,opt,name=completed" json:"completed,omitempty"` +} + +func (m *SuggestTrialsReply) Reset() { *m = SuggestTrialsReply{} } +func (m *SuggestTrialsReply) String() string { return proto.CompactTextString(m) } +func (*SuggestTrialsReply) ProtoMessage() {} +func (*SuggestTrialsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } + +func (m *SuggestTrialsReply) GetTrials() []*Trial { + if m != nil { + return m.Trials + } + return nil +} + +func (m *SuggestTrialsReply) GetCompleted() bool { + if m != nil { + return m.Completed + } + return false +} + +type CompleteTrialRequest struct { + WorkerId string `protobuf:"bytes,1,opt,name=worker_id,json=workerId" json:"worker_id,omitempty"` + IsComplete bool `protobuf:"varint,2,opt,name=is_complete,json=isComplete" json:"is_complete,omitempty"` +} + +func (m *CompleteTrialRequest) Reset() { *m = CompleteTrialRequest{} } +func (m *CompleteTrialRequest) String() string { return proto.CompactTextString(m) } +func (*CompleteTrialRequest) ProtoMessage() {} +func (*CompleteTrialRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } + +func (m *CompleteTrialRequest) GetWorkerId() string { + if m != nil { + return m.WorkerId + } + return "" +} + +func (m *CompleteTrialRequest) GetIsComplete() bool { + if m != nil { + return m.IsComplete + } + return false +} + +type CompleteTrialReply struct { +} + +func (m *CompleteTrialReply) Reset() { *m = CompleteTrialReply{} } +func (m *CompleteTrialReply) String() string { return proto.CompactTextString(m) } +func (*CompleteTrialReply) ProtoMessage() {} +func (*CompleteTrialReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } + +type ShouldTrialStopRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + AutostopAlgorithm string `protobuf:"bytes,2,opt,name=autostop_algorithm,json=autostopAlgorithm" json:"autostop_algorithm,omitempty"` +} + +func (m *ShouldTrialStopRequest) Reset() { *m = ShouldTrialStopRequest{} } +func (m *ShouldTrialStopRequest) String() string { return proto.CompactTextString(m) } +func (*ShouldTrialStopRequest) ProtoMessage() {} +func (*ShouldTrialStopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } + +func (m *ShouldTrialStopRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *ShouldTrialStopRequest) GetAutostopAlgorithm() string { + if m != nil { + return m.AutostopAlgorithm + } + return "" +} + +type ShouldTrialStopReply struct { + Trials []*Trial `protobuf:"bytes,1,rep,name=trials" json:"trials,omitempty"` + WorkerIds []string `protobuf:"bytes,2,rep,name=worker_ids,json=workerIds" json:"worker_ids,omitempty"` +} + +func (m *ShouldTrialStopReply) Reset() { *m = ShouldTrialStopReply{} } +func (m *ShouldTrialStopReply) String() string { return proto.CompactTextString(m) } +func (*ShouldTrialStopReply) ProtoMessage() {} +func (*ShouldTrialStopReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } + +func (m *ShouldTrialStopReply) GetTrials() []*Trial { + if m != nil { + return m.Trials + } + return nil +} + +func (m *ShouldTrialStopReply) GetWorkerIds() []string { + if m != nil { + return m.WorkerIds + } + return nil +} + +type GetObjectValueRequest struct { + WorkerId string `protobuf:"bytes,1,opt,name=worker_id,json=workerId" json:"worker_id,omitempty"` +} + +func (m *GetObjectValueRequest) Reset() { *m = GetObjectValueRequest{} } +func (m *GetObjectValueRequest) String() string { return proto.CompactTextString(m) } +func (*GetObjectValueRequest) ProtoMessage() {} +func (*GetObjectValueRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } + +func (m *GetObjectValueRequest) GetWorkerId() string { + if m != nil { + return m.WorkerId + } + return "" +} + +type GetObjectValueReply struct { + Trials []*Trial `protobuf:"bytes,1,rep,name=trials" json:"trials,omitempty"` +} + +func (m *GetObjectValueReply) Reset() { *m = GetObjectValueReply{} } +func (m *GetObjectValueReply) String() string { return proto.CompactTextString(m) } +func (*GetObjectValueReply) ProtoMessage() {} +func (*GetObjectValueReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *GetObjectValueReply) GetTrials() []*Trial { + if m != nil { + return m.Trials + } + return nil +} + +type AddMeasurementToTrialsRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + // metrics can be a json string + Metrics string `protobuf:"bytes,2,opt,name=metrics" json:"metrics,omitempty"` +} + +func (m *AddMeasurementToTrialsRequest) Reset() { *m = AddMeasurementToTrialsRequest{} } +func (m *AddMeasurementToTrialsRequest) String() string { return proto.CompactTextString(m) } +func (*AddMeasurementToTrialsRequest) ProtoMessage() {} +func (*AddMeasurementToTrialsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } + +func (m *AddMeasurementToTrialsRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *AddMeasurementToTrialsRequest) GetMetrics() string { + if m != nil { + return m.Metrics + } + return "" +} + +type AddMeasurementToTrialsReply struct { +} + +func (m *AddMeasurementToTrialsReply) Reset() { *m = AddMeasurementToTrialsReply{} } +func (m *AddMeasurementToTrialsReply) String() string { return proto.CompactTextString(m) } +func (*AddMeasurementToTrialsReply) ProtoMessage() {} +func (*AddMeasurementToTrialsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } + +type InitializeSuggestServiceRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + SuggestAlgorithm string `protobuf:"bytes,2,opt,name=suggest_algorithm,json=suggestAlgorithm" json:"suggest_algorithm,omitempty"` + SuggestionParameters []*SuggestionParameter `protobuf:"bytes,3,rep,name=suggestion_parameters,json=suggestionParameters" json:"suggestion_parameters,omitempty"` + Configs *StudyConfig `protobuf:"bytes,4,opt,name=configs" json:"configs,omitempty"` +} + +func (m *InitializeSuggestServiceRequest) Reset() { *m = InitializeSuggestServiceRequest{} } +func (m *InitializeSuggestServiceRequest) String() string { return proto.CompactTextString(m) } +func (*InitializeSuggestServiceRequest) ProtoMessage() {} +func (*InitializeSuggestServiceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{27} +} + +func (m *InitializeSuggestServiceRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *InitializeSuggestServiceRequest) GetSuggestAlgorithm() string { + if m != nil { + return m.SuggestAlgorithm + } + return "" +} + +func (m *InitializeSuggestServiceRequest) GetSuggestionParameters() []*SuggestionParameter { + if m != nil { + return m.SuggestionParameters + } + return nil +} + +func (m *InitializeSuggestServiceRequest) GetConfigs() *StudyConfig { + if m != nil { + return m.Configs + } + return nil +} + +type InitializeSuggestServiceReply struct { +} + +func (m *InitializeSuggestServiceReply) Reset() { *m = InitializeSuggestServiceReply{} } +func (m *InitializeSuggestServiceReply) String() string { return proto.CompactTextString(m) } +func (*InitializeSuggestServiceReply) ProtoMessage() {} +func (*InitializeSuggestServiceReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +type GenerateTrialsRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + Configs *StudyConfig `protobuf:"bytes,2,opt,name=configs" json:"configs,omitempty"` + CompletedTrials []*Trial `protobuf:"bytes,3,rep,name=completed_trials,json=completedTrials" json:"completed_trials,omitempty"` + RunningTrials []*Trial `protobuf:"bytes,4,rep,name=running_trials,json=runningTrials" json:"running_trials,omitempty"` +} + +func (m *GenerateTrialsRequest) Reset() { *m = GenerateTrialsRequest{} } +func (m *GenerateTrialsRequest) String() string { return proto.CompactTextString(m) } +func (*GenerateTrialsRequest) ProtoMessage() {} +func (*GenerateTrialsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } + +func (m *GenerateTrialsRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *GenerateTrialsRequest) GetConfigs() *StudyConfig { + if m != nil { + return m.Configs + } + return nil +} + +func (m *GenerateTrialsRequest) GetCompletedTrials() []*Trial { + if m != nil { + return m.CompletedTrials + } + return nil +} + +func (m *GenerateTrialsRequest) GetRunningTrials() []*Trial { + if m != nil { + return m.RunningTrials + } + return nil +} + +type GenerateTrialsReply struct { + Trials []*Trial `protobuf:"bytes,1,rep,name=trials" json:"trials,omitempty"` + Completed bool `protobuf:"varint,2,opt,name=completed" json:"completed,omitempty"` +} + +func (m *GenerateTrialsReply) Reset() { *m = GenerateTrialsReply{} } +func (m *GenerateTrialsReply) String() string { return proto.CompactTextString(m) } +func (*GenerateTrialsReply) ProtoMessage() {} +func (*GenerateTrialsReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } + +func (m *GenerateTrialsReply) GetTrials() []*Trial { + if m != nil { + return m.Trials + } + return nil +} + +func (m *GenerateTrialsReply) GetCompleted() bool { + if m != nil { + return m.Completed + } + return false +} + +type SetSuggestionParametersRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` + SuggestionParameters []*SuggestionParameter `protobuf:"bytes,2,rep,name=suggestion_parameters,json=suggestionParameters" json:"suggestion_parameters,omitempty"` + Configs *StudyConfig `protobuf:"bytes,3,opt,name=configs" json:"configs,omitempty"` +} + +func (m *SetSuggestionParametersRequest) Reset() { *m = SetSuggestionParametersRequest{} } +func (m *SetSuggestionParametersRequest) String() string { return proto.CompactTextString(m) } +func (*SetSuggestionParametersRequest) ProtoMessage() {} +func (*SetSuggestionParametersRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *SetSuggestionParametersRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +func (m *SetSuggestionParametersRequest) GetSuggestionParameters() []*SuggestionParameter { + if m != nil { + return m.SuggestionParameters + } + return nil +} + +func (m *SetSuggestionParametersRequest) GetConfigs() *StudyConfig { + if m != nil { + return m.Configs + } + return nil +} + +type SetSuggestionParametersReply struct { +} + +func (m *SetSuggestionParametersReply) Reset() { *m = SetSuggestionParametersReply{} } +func (m *SetSuggestionParametersReply) String() string { return proto.CompactTextString(m) } +func (*SetSuggestionParametersReply) ProtoMessage() {} +func (*SetSuggestionParametersReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +type StopSuggestionRequest struct { + StudyId string `protobuf:"bytes,1,opt,name=study_id,json=studyId" json:"study_id,omitempty"` +} + +func (m *StopSuggestionRequest) Reset() { *m = StopSuggestionRequest{} } +func (m *StopSuggestionRequest) String() string { return proto.CompactTextString(m) } +func (*StopSuggestionRequest) ProtoMessage() {} +func (*StopSuggestionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } + +func (m *StopSuggestionRequest) GetStudyId() string { + if m != nil { + return m.StudyId + } + return "" +} + +type StopSuggestionReply struct { +} + +func (m *StopSuggestionReply) Reset() { *m = StopSuggestionReply{} } +func (m *StopSuggestionReply) String() string { return proto.CompactTextString(m) } +func (*StopSuggestionReply) ProtoMessage() {} +func (*StopSuggestionReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func init() { + proto.RegisterType((*FeasibleSpace)(nil), "api.FeasibleSpace") + proto.RegisterType((*ParameterConfig)(nil), "api.ParameterConfig") + proto.RegisterType((*Parameter)(nil), "api.Parameter") + proto.RegisterType((*Metrics)(nil), "api.Metrics") + proto.RegisterType((*EvaluationLog)(nil), "api.EvaluationLog") + proto.RegisterType((*SuggestionParameter)(nil), "api.SuggestionParameter") + proto.RegisterType((*Tag)(nil), "api.Tag") + proto.RegisterType((*MountConf)(nil), "api.MountConf") + proto.RegisterType((*Trial)(nil), "api.Trial") + proto.RegisterType((*StudyConfig)(nil), "api.StudyConfig") + proto.RegisterType((*StudyConfig_ParameterConfigs)(nil), "api.StudyConfig.ParameterConfigs") + proto.RegisterType((*CreateStudyRequest)(nil), "api.CreateStudyRequest") + proto.RegisterType((*CreateStudyReply)(nil), "api.CreateStudyReply") + proto.RegisterType((*StopStudyRequest)(nil), "api.StopStudyRequest") + proto.RegisterType((*StopStudyReply)(nil), "api.StopStudyReply") + proto.RegisterType((*GetStudysRequest)(nil), "api.GetStudysRequest") + proto.RegisterType((*StudyInfo)(nil), "api.StudyInfo") + proto.RegisterType((*GetStudysReply)(nil), "api.GetStudysReply") + proto.RegisterType((*SuggestTrialsRequest)(nil), "api.SuggestTrialsRequest") + proto.RegisterType((*SuggestTrialsReply)(nil), "api.SuggestTrialsReply") + proto.RegisterType((*CompleteTrialRequest)(nil), "api.CompleteTrialRequest") + proto.RegisterType((*CompleteTrialReply)(nil), "api.CompleteTrialReply") + proto.RegisterType((*ShouldTrialStopRequest)(nil), "api.ShouldTrialStopRequest") + proto.RegisterType((*ShouldTrialStopReply)(nil), "api.ShouldTrialStopReply") + proto.RegisterType((*GetObjectValueRequest)(nil), "api.GetObjectValueRequest") + proto.RegisterType((*GetObjectValueReply)(nil), "api.GetObjectValueReply") + proto.RegisterType((*AddMeasurementToTrialsRequest)(nil), "api.AddMeasurementToTrialsRequest") + proto.RegisterType((*AddMeasurementToTrialsReply)(nil), "api.AddMeasurementToTrialsReply") + proto.RegisterType((*InitializeSuggestServiceRequest)(nil), "api.InitializeSuggestServiceRequest") + proto.RegisterType((*InitializeSuggestServiceReply)(nil), "api.InitializeSuggestServiceReply") + proto.RegisterType((*GenerateTrialsRequest)(nil), "api.GenerateTrialsRequest") + proto.RegisterType((*GenerateTrialsReply)(nil), "api.GenerateTrialsReply") + proto.RegisterType((*SetSuggestionParametersRequest)(nil), "api.SetSuggestionParametersRequest") + proto.RegisterType((*SetSuggestionParametersReply)(nil), "api.SetSuggestionParametersReply") + proto.RegisterType((*StopSuggestionRequest)(nil), "api.StopSuggestionRequest") + proto.RegisterType((*StopSuggestionReply)(nil), "api.StopSuggestionReply") + proto.RegisterEnum("api.ParameterType", ParameterType_name, ParameterType_value) + proto.RegisterEnum("api.OptimizationType", OptimizationType_name, OptimizationType_value) + proto.RegisterEnum("api.TrialState", TrialState_name, TrialState_value) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Manager service + +type ManagerClient interface { + CreateStudy(ctx context.Context, in *CreateStudyRequest, opts ...grpc.CallOption) (*CreateStudyReply, error) + StopStudy(ctx context.Context, in *StopStudyRequest, opts ...grpc.CallOption) (*StopStudyReply, error) + GetStudys(ctx context.Context, in *GetStudysRequest, opts ...grpc.CallOption) (*GetStudysReply, error) + SuggestTrials(ctx context.Context, in *SuggestTrialsRequest, opts ...grpc.CallOption) (*SuggestTrialsReply, error) + CompleteTrial(ctx context.Context, in *CompleteTrialRequest, opts ...grpc.CallOption) (*CompleteTrialReply, error) + ShouldTrialStop(ctx context.Context, in *ShouldTrialStopRequest, opts ...grpc.CallOption) (*ShouldTrialStopReply, error) + GetObjectValue(ctx context.Context, in *GetObjectValueRequest, opts ...grpc.CallOption) (*GetObjectValueReply, error) + AddMeasurementToTrials(ctx context.Context, in *AddMeasurementToTrialsRequest, opts ...grpc.CallOption) (*AddMeasurementToTrialsReply, error) + InitializeSuggestService(ctx context.Context, in *InitializeSuggestServiceRequest, opts ...grpc.CallOption) (*InitializeSuggestServiceReply, error) +} + +type managerClient struct { + cc *grpc.ClientConn +} + +func NewManagerClient(cc *grpc.ClientConn) ManagerClient { + return &managerClient{cc} +} + +func (c *managerClient) CreateStudy(ctx context.Context, in *CreateStudyRequest, opts ...grpc.CallOption) (*CreateStudyReply, error) { + out := new(CreateStudyReply) + err := grpc.Invoke(ctx, "/api.Manager/CreateStudy", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) StopStudy(ctx context.Context, in *StopStudyRequest, opts ...grpc.CallOption) (*StopStudyReply, error) { + out := new(StopStudyReply) + err := grpc.Invoke(ctx, "/api.Manager/StopStudy", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) GetStudys(ctx context.Context, in *GetStudysRequest, opts ...grpc.CallOption) (*GetStudysReply, error) { + out := new(GetStudysReply) + err := grpc.Invoke(ctx, "/api.Manager/GetStudys", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) SuggestTrials(ctx context.Context, in *SuggestTrialsRequest, opts ...grpc.CallOption) (*SuggestTrialsReply, error) { + out := new(SuggestTrialsReply) + err := grpc.Invoke(ctx, "/api.Manager/SuggestTrials", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) CompleteTrial(ctx context.Context, in *CompleteTrialRequest, opts ...grpc.CallOption) (*CompleteTrialReply, error) { + out := new(CompleteTrialReply) + err := grpc.Invoke(ctx, "/api.Manager/CompleteTrial", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) ShouldTrialStop(ctx context.Context, in *ShouldTrialStopRequest, opts ...grpc.CallOption) (*ShouldTrialStopReply, error) { + out := new(ShouldTrialStopReply) + err := grpc.Invoke(ctx, "/api.Manager/ShouldTrialStop", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) GetObjectValue(ctx context.Context, in *GetObjectValueRequest, opts ...grpc.CallOption) (*GetObjectValueReply, error) { + out := new(GetObjectValueReply) + err := grpc.Invoke(ctx, "/api.Manager/GetObjectValue", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) AddMeasurementToTrials(ctx context.Context, in *AddMeasurementToTrialsRequest, opts ...grpc.CallOption) (*AddMeasurementToTrialsReply, error) { + out := new(AddMeasurementToTrialsReply) + err := grpc.Invoke(ctx, "/api.Manager/AddMeasurementToTrials", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *managerClient) InitializeSuggestService(ctx context.Context, in *InitializeSuggestServiceRequest, opts ...grpc.CallOption) (*InitializeSuggestServiceReply, error) { + out := new(InitializeSuggestServiceReply) + err := grpc.Invoke(ctx, "/api.Manager/InitializeSuggestService", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Manager service + +type ManagerServer interface { + CreateStudy(context.Context, *CreateStudyRequest) (*CreateStudyReply, error) + StopStudy(context.Context, *StopStudyRequest) (*StopStudyReply, error) + GetStudys(context.Context, *GetStudysRequest) (*GetStudysReply, error) + SuggestTrials(context.Context, *SuggestTrialsRequest) (*SuggestTrialsReply, error) + CompleteTrial(context.Context, *CompleteTrialRequest) (*CompleteTrialReply, error) + ShouldTrialStop(context.Context, *ShouldTrialStopRequest) (*ShouldTrialStopReply, error) + GetObjectValue(context.Context, *GetObjectValueRequest) (*GetObjectValueReply, error) + AddMeasurementToTrials(context.Context, *AddMeasurementToTrialsRequest) (*AddMeasurementToTrialsReply, error) + InitializeSuggestService(context.Context, *InitializeSuggestServiceRequest) (*InitializeSuggestServiceReply, error) +} + +func RegisterManagerServer(s *grpc.Server, srv ManagerServer) { + s.RegisterService(&_Manager_serviceDesc, srv) +} + +func _Manager_CreateStudy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateStudyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).CreateStudy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/CreateStudy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).CreateStudy(ctx, req.(*CreateStudyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_StopStudy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopStudyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).StopStudy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/StopStudy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).StopStudy(ctx, req.(*StopStudyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_GetStudys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetStudysRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).GetStudys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/GetStudys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).GetStudys(ctx, req.(*GetStudysRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_SuggestTrials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SuggestTrialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).SuggestTrials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/SuggestTrials", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).SuggestTrials(ctx, req.(*SuggestTrialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_CompleteTrial_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteTrialRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).CompleteTrial(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/CompleteTrial", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).CompleteTrial(ctx, req.(*CompleteTrialRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_ShouldTrialStop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ShouldTrialStopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).ShouldTrialStop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/ShouldTrialStop", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).ShouldTrialStop(ctx, req.(*ShouldTrialStopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_GetObjectValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetObjectValueRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).GetObjectValue(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/GetObjectValue", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).GetObjectValue(ctx, req.(*GetObjectValueRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_AddMeasurementToTrials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddMeasurementToTrialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).AddMeasurementToTrials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/AddMeasurementToTrials", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).AddMeasurementToTrials(ctx, req.(*AddMeasurementToTrialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Manager_InitializeSuggestService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitializeSuggestServiceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ManagerServer).InitializeSuggestService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Manager/InitializeSuggestService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ManagerServer).InitializeSuggestService(ctx, req.(*InitializeSuggestServiceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Manager_serviceDesc = grpc.ServiceDesc{ + ServiceName: "api.Manager", + HandlerType: (*ManagerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateStudy", + Handler: _Manager_CreateStudy_Handler, + }, + { + MethodName: "StopStudy", + Handler: _Manager_StopStudy_Handler, + }, + { + MethodName: "GetStudys", + Handler: _Manager_GetStudys_Handler, + }, + { + MethodName: "SuggestTrials", + Handler: _Manager_SuggestTrials_Handler, + }, + { + MethodName: "CompleteTrial", + Handler: _Manager_CompleteTrial_Handler, + }, + { + MethodName: "ShouldTrialStop", + Handler: _Manager_ShouldTrialStop_Handler, + }, + { + MethodName: "GetObjectValue", + Handler: _Manager_GetObjectValue_Handler, + }, + { + MethodName: "AddMeasurementToTrials", + Handler: _Manager_AddMeasurementToTrials_Handler, + }, + { + MethodName: "InitializeSuggestService", + Handler: _Manager_InitializeSuggestService_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +// Client API for Suggestion service + +type SuggestionClient interface { + GenerateTrials(ctx context.Context, in *GenerateTrialsRequest, opts ...grpc.CallOption) (*GenerateTrialsReply, error) + SetSuggestionParameters(ctx context.Context, in *SetSuggestionParametersRequest, opts ...grpc.CallOption) (*SetSuggestionParametersReply, error) + StopSuggestion(ctx context.Context, in *StopSuggestionRequest, opts ...grpc.CallOption) (*StopSuggestionReply, error) +} + +type suggestionClient struct { + cc *grpc.ClientConn +} + +func NewSuggestionClient(cc *grpc.ClientConn) SuggestionClient { + return &suggestionClient{cc} +} + +func (c *suggestionClient) GenerateTrials(ctx context.Context, in *GenerateTrialsRequest, opts ...grpc.CallOption) (*GenerateTrialsReply, error) { + out := new(GenerateTrialsReply) + err := grpc.Invoke(ctx, "/api.Suggestion/GenerateTrials", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *suggestionClient) SetSuggestionParameters(ctx context.Context, in *SetSuggestionParametersRequest, opts ...grpc.CallOption) (*SetSuggestionParametersReply, error) { + out := new(SetSuggestionParametersReply) + err := grpc.Invoke(ctx, "/api.Suggestion/SetSuggestionParameters", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *suggestionClient) StopSuggestion(ctx context.Context, in *StopSuggestionRequest, opts ...grpc.CallOption) (*StopSuggestionReply, error) { + out := new(StopSuggestionReply) + err := grpc.Invoke(ctx, "/api.Suggestion/StopSuggestion", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Suggestion service + +type SuggestionServer interface { + GenerateTrials(context.Context, *GenerateTrialsRequest) (*GenerateTrialsReply, error) + SetSuggestionParameters(context.Context, *SetSuggestionParametersRequest) (*SetSuggestionParametersReply, error) + StopSuggestion(context.Context, *StopSuggestionRequest) (*StopSuggestionReply, error) +} + +func RegisterSuggestionServer(s *grpc.Server, srv SuggestionServer) { + s.RegisterService(&_Suggestion_serviceDesc, srv) +} + +func _Suggestion_GenerateTrials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateTrialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SuggestionServer).GenerateTrials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Suggestion/GenerateTrials", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SuggestionServer).GenerateTrials(ctx, req.(*GenerateTrialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Suggestion_SetSuggestionParameters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetSuggestionParametersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SuggestionServer).SetSuggestionParameters(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Suggestion/SetSuggestionParameters", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SuggestionServer).SetSuggestionParameters(ctx, req.(*SetSuggestionParametersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Suggestion_StopSuggestion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopSuggestionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SuggestionServer).StopSuggestion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.Suggestion/StopSuggestion", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SuggestionServer).StopSuggestion(ctx, req.(*StopSuggestionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Suggestion_serviceDesc = grpc.ServiceDesc{ + ServiceName: "api.Suggestion", + HandlerType: (*SuggestionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GenerateTrials", + Handler: _Suggestion_GenerateTrials_Handler, + }, + { + MethodName: "SetSuggestionParameters", + Handler: _Suggestion_SetSuggestionParameters_Handler, + }, + { + MethodName: "StopSuggestion", + Handler: _Suggestion_StopSuggestion_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +// Client API for AutoStopping service + +type AutoStoppingClient interface { +} + +type autoStoppingClient struct { + cc *grpc.ClientConn +} + +func NewAutoStoppingClient(cc *grpc.ClientConn) AutoStoppingClient { + return &autoStoppingClient{cc} +} + +// Server API for AutoStopping service + +type AutoStoppingServer interface { +} + +func RegisterAutoStoppingServer(s *grpc.Server, srv AutoStoppingServer) { + s.RegisterService(&_AutoStopping_serviceDesc, srv) +} + +var _AutoStopping_serviceDesc = grpc.ServiceDesc{ + ServiceName: "api.AutoStopping", + HandlerType: (*AutoStoppingServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func init() { proto.RegisterFile("api.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 1690 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xdd, 0x72, 0xdb, 0xc6, + 0x15, 0x36, 0x48, 0x51, 0x24, 0x0f, 0x45, 0x12, 0x5a, 0x51, 0x31, 0x4c, 0xdb, 0xb1, 0x82, 0x66, + 0x12, 0x8d, 0x3a, 0x91, 0x1b, 0xba, 0x9d, 0x4e, 0x2e, 0x3a, 0x1d, 0x9a, 0x62, 0x54, 0x8c, 0xf9, + 0xa3, 0x01, 0xe9, 0xa4, 0xe9, 0x45, 0x39, 0x6b, 0x72, 0x4d, 0xa3, 0x06, 0x01, 0x14, 0xbb, 0x50, + 0xa2, 0x3c, 0x42, 0xaf, 0x3a, 0xd3, 0xeb, 0xbe, 0x41, 0xaf, 0xfb, 0x0c, 0xbd, 0xeb, 0x4b, 0xf4, + 0x3d, 0xda, 0xd9, 0xc5, 0x02, 0x04, 0x40, 0x90, 0x62, 0x5b, 0xdf, 0xed, 0x9e, 0x3f, 0x9c, 0xdf, + 0xef, 0x2c, 0x09, 0x55, 0xec, 0x59, 0x97, 0x9e, 0xef, 0x32, 0x17, 0x15, 0xb1, 0x67, 0xe9, 0xd7, + 0x50, 0xff, 0x9a, 0x60, 0x6a, 0xbd, 0xb1, 0xc9, 0xc4, 0xc3, 0x73, 0x82, 0x54, 0x28, 0xae, 0xf0, + 0x0f, 0x9a, 0x72, 0xa6, 0x9c, 0x57, 0x4d, 0x7e, 0x14, 0x14, 0xcb, 0xd1, 0x0a, 0x92, 0x62, 0x39, + 0x08, 0xc1, 0x81, 0x6d, 0x51, 0xa6, 0x15, 0xcf, 0x8a, 0xe7, 0x55, 0x53, 0x9c, 0xf5, 0x3f, 0x2b, + 0xd0, 0xbc, 0xc1, 0x3e, 0x5e, 0x11, 0x46, 0xfc, 0x9e, 0xeb, 0xbc, 0xb5, 0x96, 0x5c, 0xce, 0xc1, + 0x2b, 0x22, 0x8d, 0x89, 0x33, 0xfa, 0x0a, 0x1a, 0x5e, 0x24, 0x36, 0x63, 0x77, 0x1e, 0x11, 0x86, + 0x1b, 0x1d, 0x74, 0xc9, 0x3d, 0x8b, 0x2d, 0x4c, 0xef, 0x3c, 0x62, 0xd6, 0xbd, 0xe4, 0x15, 0x5d, + 0x42, 0xe5, 0xad, 0xf4, 0x55, 0x2b, 0x9e, 0x29, 0xe7, 0x35, 0xa9, 0x94, 0x0a, 0xc0, 0x8c, 0x65, + 0x74, 0x0f, 0xaa, 0xb1, 0xbd, 0x0f, 0xed, 0x4b, 0x0b, 0x4a, 0xb7, 0xd8, 0x0e, 0x42, 0x47, 0xaa, + 0x66, 0x78, 0xd1, 0x5f, 0x40, 0x79, 0x48, 0x98, 0x6f, 0xcd, 0x69, 0xee, 0xf7, 0x62, 0xa5, 0x42, + 0x52, 0xe9, 0x15, 0xd4, 0xfb, 0xfc, 0x84, 0x99, 0xe5, 0x3a, 0x03, 0x57, 0xa4, 0x8d, 0x59, 0x6b, + 0x55, 0x7e, 0x46, 0x9f, 0x41, 0x79, 0x15, 0x5a, 0xd6, 0x0a, 0x67, 0xc5, 0xf3, 0x5a, 0xe7, 0x48, + 0xf8, 0x28, 0xbf, 0x66, 0x46, 0x4c, 0xfd, 0xd7, 0x70, 0x32, 0x09, 0x96, 0x4b, 0x42, 0xb9, 0xb1, + 0xdd, 0xd1, 0xe7, 0x7b, 0xf3, 0x1c, 0x8a, 0x53, 0xbc, 0xfc, 0x2f, 0x14, 0xbe, 0x84, 0xea, 0xd0, + 0x0d, 0x1c, 0xc6, 0x6b, 0xce, 0x7b, 0xc5, 0xbb, 0x9d, 0x47, 0xdd, 0xe3, 0xdd, 0xce, 0xb9, 0x21, + 0x0f, 0xb3, 0x77, 0x52, 0x47, 0x9c, 0xf5, 0xbf, 0x14, 0xa0, 0x34, 0xf5, 0x2d, 0x6c, 0xa3, 0x47, + 0x50, 0x61, 0xfc, 0x30, 0xb3, 0x16, 0x52, 0xa9, 0x2c, 0xee, 0xc6, 0x82, 0xb3, 0x28, 0x0b, 0x16, + 0x77, 0x9c, 0x15, 0x2a, 0x97, 0xc5, 0xdd, 0x58, 0xa0, 0x17, 0xb0, 0xae, 0xc6, 0x8c, 0x92, 0xb0, + 0x11, 0x6b, 0x9d, 0x46, 0xba, 0x6c, 0xe6, 0x51, 0x2c, 0x34, 0x21, 0x0c, 0x7d, 0x0e, 0x87, 0x94, + 0x61, 0x16, 0x50, 0xed, 0x40, 0x14, 0xb9, 0x29, 0xa4, 0x85, 0x1b, 0x13, 0x86, 0x19, 0x31, 0x25, + 0x1b, 0x3d, 0x87, 0x2a, 0xb9, 0xc5, 0xf6, 0xcc, 0x76, 0x97, 0x54, 0x2b, 0x09, 0xcb, 0x61, 0x43, + 0xa4, 0xaa, 0x64, 0x56, 0xb8, 0xd0, 0xc0, 0x5d, 0x52, 0xf4, 0x39, 0x34, 0xdd, 0x37, 0x7f, 0x20, + 0x73, 0x66, 0xdd, 0x92, 0x59, 0x98, 0xa1, 0x43, 0xe1, 0x70, 0x23, 0x26, 0x7f, 0xc3, 0xa9, 0xe8, + 0x09, 0x1c, 0x30, 0xbc, 0xa4, 0x5a, 0x59, 0x18, 0xad, 0x84, 0x0e, 0xe0, 0xa5, 0x29, 0xa8, 0xfa, + 0x3f, 0x0f, 0xa1, 0x36, 0xe1, 0x11, 0xee, 0x98, 0x9e, 0x16, 0x94, 0xdc, 0xef, 0x1d, 0xe2, 0x47, + 0x25, 0x10, 0x17, 0xf4, 0x12, 0x8e, 0x5d, 0x8f, 0x59, 0x2b, 0xeb, 0x47, 0xe1, 0x5d, 0xd8, 0xca, + 0x45, 0x11, 0xe5, 0xa9, 0xf8, 0xc8, 0x38, 0xc1, 0x15, 0xdd, 0xac, 0xba, 0x19, 0x0a, 0xfa, 0x69, + 0xc6, 0xc6, 0xd2, 0xc5, 0xb6, 0xc8, 0x94, 0x92, 0x16, 0xbe, 0x76, 0xb1, 0x8d, 0x46, 0x70, 0xbc, + 0x2e, 0xc0, 0x5c, 0xb8, 0xcb, 0x53, 0xc5, 0x47, 0xf2, 0x13, 0xf1, 0xc1, 0x44, 0x1c, 0x97, 0x19, + 0x54, 0xa0, 0xa6, 0xea, 0x65, 0x28, 0xe8, 0x0b, 0x40, 0x78, 0x3e, 0x27, 0x94, 0xce, 0x3c, 0xe2, + 0xaf, 0x2c, 0x4a, 0x2d, 0xd7, 0xa1, 0xda, 0xa1, 0x80, 0x97, 0xe3, 0x90, 0x73, 0xb3, 0x66, 0x70, + 0x5f, 0x69, 0xd8, 0xe4, 0x33, 0x6c, 0x2f, 0x5d, 0xdf, 0x62, 0xef, 0x56, 0x5a, 0x59, 0x64, 0x44, + 0x95, 0x8c, 0x6e, 0x44, 0x17, 0xb6, 0x03, 0xe6, 0x52, 0xe6, 0x7a, 0x09, 0xe9, 0x8a, 0x90, 0x3e, + 0x8e, 0x38, 0x6b, 0xf1, 0xcf, 0xa0, 0x19, 0xb6, 0x1d, 0xc3, 0xf4, 0xfd, 0x4c, 0x14, 0xa0, 0x2a, + 0x64, 0xeb, 0x82, 0x3c, 0xc5, 0xf4, 0xfd, 0x88, 0x57, 0x62, 0x08, 0xa7, 0x34, 0x1e, 0xb4, 0x59, + 0x1c, 0x11, 0xd5, 0x40, 0x14, 0x57, 0x0b, 0xd3, 0xb0, 0x39, 0x8a, 0x66, 0x8b, 0x6e, 0x12, 0x69, + 0xdc, 0x1a, 0xb5, 0xbc, 0xd6, 0x40, 0x3f, 0x83, 0x56, 0xa6, 0xc3, 0x42, 0xcf, 0x8e, 0x84, 0x67, + 0x28, 0xdd, 0x66, 0xc2, 0x3d, 0x6d, 0x8d, 0x17, 0x75, 0x91, 0xc6, 0xe8, 0xca, 0x5b, 0xc8, 0x5a, + 0xe1, 0x25, 0xd1, 0x1a, 0x61, 0x0b, 0x89, 0x0b, 0x97, 0x9f, 0xbb, 0xab, 0x15, 0x76, 0x16, 0x5a, + 0x33, 0x94, 0x97, 0x57, 0x3e, 0xd2, 0x4b, 0x2f, 0xd0, 0xd4, 0x33, 0xe5, 0xbc, 0x64, 0xf2, 0x23, + 0x7a, 0x02, 0x55, 0x3a, 0x7f, 0x47, 0x16, 0x81, 0x4d, 0x7c, 0xed, 0x58, 0x58, 0x59, 0x13, 0xd0, + 0xa7, 0x50, 0x5a, 0x71, 0x3c, 0xd0, 0x90, 0xe8, 0x87, 0x70, 0x28, 0x63, 0x84, 0x30, 0x43, 0x26, + 0x7a, 0x06, 0x35, 0x2f, 0xb0, 0xed, 0x19, 0x25, 0x73, 0x9f, 0x30, 0xed, 0x44, 0x58, 0x01, 0x4e, + 0x9a, 0x08, 0x4a, 0xfb, 0x25, 0xa8, 0xd9, 0xc6, 0x41, 0x97, 0xdc, 0xc9, 0xb0, 0xd9, 0x14, 0x91, + 0xa7, 0x56, 0x7a, 0xe2, 0x43, 0x39, 0x33, 0x12, 0xd2, 0x0d, 0x40, 0x3d, 0x9f, 0x60, 0x46, 0x44, + 0x3b, 0x9a, 0xe4, 0x8f, 0x01, 0xa1, 0x0c, 0xbd, 0x80, 0xa3, 0xb0, 0xc2, 0xa1, 0x98, 0x98, 0xaf, + 0x5a, 0x47, 0xcd, 0xf6, 0xad, 0x59, 0xa3, 0xeb, 0x8b, 0xfe, 0x05, 0xa8, 0x29, 0x53, 0x9e, 0x7d, + 0x97, 0x42, 0x28, 0x25, 0x85, 0x50, 0x5c, 0x7c, 0xc2, 0x5c, 0x2f, 0xf5, 0xdd, 0x1d, 0xe2, 0x2a, + 0x34, 0x12, 0xe2, 0x9e, 0x7d, 0xa7, 0x23, 0x50, 0xaf, 0x09, 0x13, 0x04, 0x2a, 0x0d, 0xe8, 0x7f, + 0x53, 0xa0, 0x2a, 0x28, 0x86, 0xf3, 0xd6, 0xdd, 0x61, 0x2e, 0x46, 0x8e, 0x42, 0x1e, 0x72, 0x14, + 0x93, 0xc8, 0x71, 0x01, 0xc7, 0x7e, 0xe0, 0x38, 0x96, 0xb3, 0x9c, 0x85, 0x38, 0xec, 0x04, 0x2b, + 0x31, 0xf5, 0x25, 0xb3, 0x29, 0x19, 0x02, 0x21, 0x47, 0xc1, 0x0a, 0x5d, 0xc2, 0xc9, 0xdc, 0x5d, + 0x79, 0x36, 0x61, 0x64, 0x91, 0x90, 0x2e, 0x09, 0xe9, 0xe3, 0x98, 0x15, 0xc9, 0xeb, 0x5d, 0x68, + 0x24, 0x42, 0xe0, 0x09, 0x7b, 0x0e, 0x35, 0xe9, 0xb2, 0xf3, 0xd6, 0x8d, 0x6a, 0xd8, 0x58, 0x27, + 0x9e, 0xc7, 0x65, 0x02, 0x8d, 0x8e, 0x54, 0xff, 0x93, 0x02, 0x2d, 0x39, 0x43, 0xc2, 0x2c, 0xbd, + 0x3f, 0x97, 0xf9, 0xe0, 0x50, 0xd8, 0x02, 0x0e, 0x17, 0xeb, 0x8e, 0x2a, 0x6e, 0x69, 0x83, 0xb8, + 0x9b, 0xbe, 0x01, 0x94, 0xf1, 0x85, 0xc7, 0xa4, 0xc3, 0xa1, 0xc8, 0x45, 0x14, 0x0e, 0xac, 0xd7, + 0x8a, 0x29, 0x39, 0x7c, 0x60, 0xe2, 0xf4, 0x08, 0x57, 0x2a, 0xe6, 0x9a, 0xa0, 0x4f, 0xa1, 0xd5, + 0x93, 0x97, 0x50, 0x4d, 0xc6, 0xf8, 0x18, 0xaa, 0xdf, 0xbb, 0xfe, 0x7b, 0xe2, 0xaf, 0x83, 0xac, + 0x84, 0x04, 0x63, 0xc1, 0xe7, 0xc7, 0xa2, 0xb3, 0xc8, 0x88, 0x34, 0x0a, 0x16, 0x8d, 0x2c, 0xe9, + 0x2d, 0x40, 0x19, 0xab, 0xbc, 0xad, 0xde, 0xc0, 0x47, 0x93, 0x77, 0x6e, 0x60, 0x2f, 0xe4, 0xde, + 0x73, 0xbd, 0x3d, 0x32, 0x9a, 0x8f, 0xa0, 0x85, 0x2d, 0x08, 0xaa, 0x7f, 0x07, 0xad, 0x8d, 0x6f, + 0xec, 0x9b, 0xa9, 0xa7, 0x00, 0x71, 0xcc, 0xe1, 0x4b, 0xa7, 0x6a, 0x56, 0xa3, 0xa0, 0xa9, 0xfe, + 0x73, 0x38, 0xbd, 0x26, 0x6c, 0x2c, 0xe0, 0x4e, 0x60, 0xdd, 0x3e, 0xb9, 0xd2, 0xbf, 0x82, 0x93, + 0xac, 0xd6, 0x9e, 0xfe, 0xe8, 0x53, 0x78, 0xda, 0x5d, 0x2c, 0x86, 0x04, 0xd3, 0xc0, 0x27, 0x2b, + 0xe2, 0xb0, 0xa9, 0xbb, 0x77, 0x23, 0x6a, 0xc9, 0x27, 0x9b, 0x92, 0x80, 0x60, 0xfd, 0x29, 0x3c, + 0xde, 0x66, 0x95, 0x17, 0xe9, 0x5f, 0x0a, 0x3c, 0x33, 0x1c, 0x8b, 0x59, 0xd8, 0xb6, 0x7e, 0x24, + 0xb2, 0xe7, 0x26, 0xc4, 0xbf, 0xb5, 0xe6, 0xe4, 0x43, 0x0f, 0xc0, 0xd6, 0x35, 0x56, 0xfc, 0x9f, + 0xd6, 0x58, 0x62, 0x9e, 0x0e, 0xee, 0x9b, 0xa7, 0x67, 0xf0, 0x74, 0x7b, 0x94, 0x3c, 0x0f, 0xff, + 0x50, 0x78, 0xb9, 0x1d, 0xe2, 0x63, 0xd9, 0xc3, 0xfb, 0x64, 0x3d, 0xe1, 0x41, 0xe1, 0x1e, 0x0f, + 0xd0, 0x2f, 0x40, 0xcd, 0x20, 0x5a, 0x14, 0x77, 0xb2, 0x17, 0x9a, 0x69, 0x68, 0xa3, 0xe8, 0x4b, + 0x68, 0xa4, 0x40, 0x93, 0xc7, 0x9a, 0x55, 0xaa, 0x27, 0xd1, 0x93, 0xea, 0xdf, 0xf2, 0x16, 0x4c, + 0x47, 0xf2, 0x61, 0xc0, 0xe3, 0xef, 0x0a, 0x7c, 0x3c, 0x21, 0x2c, 0xa7, 0x42, 0xfb, 0x24, 0x6b, + 0x6b, 0xf5, 0x0b, 0xff, 0x6f, 0xf5, 0xef, 0x45, 0xd3, 0x8f, 0xe1, 0xc9, 0x56, 0xbf, 0x79, 0xf1, + 0x3b, 0x70, 0x2a, 0x56, 0x62, 0x2c, 0xb0, 0xc7, 0x1a, 0x3d, 0x85, 0x93, 0xac, 0x8e, 0x67, 0xdf, + 0x5d, 0xbc, 0x86, 0x7a, 0xea, 0xb7, 0x1c, 0x52, 0xe1, 0xe8, 0xf5, 0xe8, 0xd5, 0x68, 0xfc, 0xed, + 0x68, 0x36, 0xfd, 0xee, 0xa6, 0xaf, 0x3e, 0x40, 0x00, 0x87, 0x57, 0xe3, 0xd7, 0x2f, 0x07, 0x7d, + 0x55, 0x41, 0x65, 0x28, 0x1a, 0xa3, 0xa9, 0x5a, 0x40, 0x47, 0x50, 0xb9, 0x32, 0x26, 0x3d, 0xb3, + 0x3f, 0xed, 0xab, 0x45, 0xd4, 0x84, 0x5a, 0xaf, 0x3b, 0xed, 0x5f, 0x8f, 0x4d, 0xa3, 0xd7, 0x1d, + 0xa8, 0x07, 0x17, 0xbf, 0x01, 0x35, 0xfb, 0xae, 0x46, 0x1a, 0xb4, 0x22, 0xcb, 0xe3, 0x9b, 0xa9, + 0x31, 0x34, 0x7e, 0xd7, 0x9d, 0x1a, 0xe3, 0x91, 0xfa, 0x80, 0x1b, 0x1b, 0x1a, 0x23, 0x4e, 0xe1, + 0xdf, 0xe0, 0xb7, 0xee, 0x6f, 0xc3, 0x5b, 0xe1, 0x62, 0x00, 0xb0, 0xfe, 0x1d, 0x82, 0x6a, 0x50, + 0xbe, 0xe9, 0x8f, 0xae, 0x8c, 0xd1, 0xb5, 0xfa, 0x80, 0x5f, 0xcc, 0xd7, 0xa3, 0x11, 0xbf, 0x28, + 0xa8, 0x0e, 0xd5, 0xde, 0x78, 0x78, 0x33, 0xe8, 0x4f, 0xfb, 0x57, 0x6a, 0x81, 0x3b, 0xfd, 0xca, + 0x18, 0x0c, 0xfa, 0x57, 0x6a, 0x11, 0x55, 0xa1, 0xd4, 0x37, 0xcd, 0xb1, 0xa9, 0xfe, 0xd0, 0xf9, + 0x6b, 0x09, 0xca, 0x43, 0xec, 0xe0, 0x25, 0xf1, 0xd1, 0xaf, 0xa0, 0x96, 0x78, 0xb6, 0xa0, 0x87, + 0xa2, 0x1e, 0x9b, 0x6f, 0xa2, 0xf6, 0xe9, 0x26, 0x83, 0xf7, 0xe7, 0x2f, 0xf9, 0x83, 0x43, 0xbe, + 0x4b, 0xd0, 0xa9, 0x2c, 0x66, 0xfa, 0x59, 0xd3, 0x3e, 0xc9, 0x92, 0xa5, 0x62, 0xbc, 0xfb, 0xa5, + 0x62, 0xf6, 0x39, 0x23, 0x15, 0x33, 0x4f, 0x84, 0x1e, 0xd4, 0x53, 0x4b, 0x16, 0x3d, 0x4a, 0xf6, + 0x60, 0x0a, 0x05, 0xda, 0x0f, 0xf3, 0x58, 0xd2, 0x48, 0x6a, 0xf7, 0x49, 0x23, 0x79, 0x5b, 0x56, + 0x1a, 0xd9, 0x5c, 0x95, 0xc8, 0x80, 0x66, 0x66, 0x8d, 0xa1, 0xc7, 0xe1, 0x07, 0x73, 0x17, 0x68, + 0xfb, 0x51, 0x3e, 0x93, 0x9b, 0xfa, 0x5a, 0xbc, 0x84, 0x12, 0x0b, 0x08, 0xb5, 0xa3, 0xd8, 0x37, + 0x77, 0x59, 0x5b, 0xcb, 0xe5, 0x71, 0x3b, 0xbf, 0x87, 0x8f, 0xf2, 0xf7, 0x06, 0xd2, 0x85, 0xce, + 0xce, 0x55, 0xd5, 0x3e, 0xdb, 0x29, 0xc3, 0xed, 0x2f, 0x40, 0xdb, 0x86, 0xc8, 0xe8, 0x53, 0xa1, + 0x7d, 0xcf, 0x5a, 0x6a, 0xeb, 0xf7, 0x48, 0x79, 0xf6, 0x5d, 0xe7, 0xdf, 0x0a, 0xc0, 0x7a, 0x44, + 0xc3, 0xe4, 0x24, 0xa1, 0x31, 0x4e, 0x4e, 0x0e, 0xf2, 0xc7, 0xc9, 0xd9, 0xc4, 0x52, 0x0c, 0x0f, + 0xb7, 0x00, 0x0a, 0xfa, 0x49, 0x58, 0x9a, 0x9d, 0x30, 0xd9, 0xfe, 0x64, 0xb7, 0x90, 0xac, 0x63, + 0x1a, 0x5f, 0xa4, 0xab, 0xb9, 0x40, 0x25, 0x5d, 0xcd, 0x01, 0xa4, 0x4e, 0x03, 0x8e, 0xba, 0x01, + 0x73, 0x39, 0xcb, 0xb3, 0x9c, 0xe5, 0x9b, 0x43, 0xf1, 0x87, 0xdc, 0x8b, 0xff, 0x04, 0x00, 0x00, + 0xff, 0xff, 0x86, 0x76, 0x0e, 0x54, 0x9d, 0x13, 0x00, 0x00, +} diff --git a/api/api.proto b/api/api.proto new file mode 100644 index 00000000000..0e9cec99882 --- /dev/null +++ b/api/api.proto @@ -0,0 +1,245 @@ +syntax = "proto3"; + +package api; + +service Manager { + rpc CreateStudy(CreateStudyRequest) returns (CreateStudyReply); + rpc StopStudy(StopStudyRequest) returns (StopStudyReply); + rpc GetStudys(GetStudysRequest) returns (GetStudysReply); + rpc SuggestTrials(SuggestTrialsRequest) returns (SuggestTrialsReply); + rpc CompleteTrial(CompleteTrialRequest) returns (CompleteTrialReply); + rpc ShouldTrialStop(ShouldTrialStopRequest) returns (ShouldTrialStopReply); + rpc GetObjectValue(GetObjectValueRequest) returns (GetObjectValueReply); + rpc AddMeasurementToTrials(AddMeasurementToTrialsRequest) returns (AddMeasurementToTrialsReply); + rpc InitializeSuggestService(InitializeSuggestServiceRequest) returns(InitializeSuggestServiceReply); +} + +service Suggestion { + rpc GenerateTrials(GenerateTrialsRequest) returns (GenerateTrialsReply); + rpc SetSuggestionParameters(SetSuggestionParametersRequest) returns (SetSuggestionParametersReply); + rpc StopSuggestion(StopSuggestionRequest) returns (StopSuggestionReply); +} + +service AutoStopping { +} + +enum ParameterType { + // Not used + UNKNOWN_TYPE = 0; + + DOUBLE = 1; + INT = 2; + DISCRETE = 3; + CATEGORICAL = 4; +} + +enum OptimizationType { + // Not used + UNKNOWN_OPTIMIZATION = 0; + + MINIMIZE = 1; + MAXIMIZE = 2; +} + +message FeasibleSpace { + string max = 1; + string min = 2; + repeated string list = 3; +} + +message ParameterConfig { + string name = 1; + ParameterType parameter_type = 2; + // The following values defines a feasible parameter space. + FeasibleSpace feasible = 3; +} + +message Parameter { + string name = 1; + ParameterType parameter_type = 2; + string value = 3; +} + +// This value is stored as TINYINT in MySQL. +enum TrialState { + PENDING = 0; + RUNNING = 1; + COMPLETED = 2; + KILLED = 3; + ERROR = 120; +} + +message Metrics { + string name = 1; + string value = 2; +} + +message EvaluationLog { + string time = 1; + repeated Metrics metrics = 2; +} + +message SuggestionParameter { + string name = 1; + string value = 2; +} + +message Tag { + string name = 1; + string value = 2; +} + +message MountConf { + string pvc = 1; + string path = 2; +} + +message Trial { + string trial_id = 1; + string study_id = 2; + repeated Parameter parameter_set = 3; + TrialState status = 4; + repeated EvaluationLog eval_logs = 5; + string objective_value = 6; + repeated Tag tags = 7; +} + +message StudyConfig { + message ParameterConfigs { + repeated ParameterConfig configs = 1; + } + string name = 1; + string owner = 2; + OptimizationType optimization_type = 3; + double optimization_goal = 4; + ParameterConfigs parameter_configs = 5; + repeated string access_permissions = 6; + string suggest_algorithm = 7; + string autostop_algorithm = 8; + string study_task_name = 9; + repeated SuggestionParameter suggestion_parameters =10; + repeated Tag tags = 11; + string objective_value_name = 12; + repeated string metrics = 13; + string image = 14; + repeated string command = 15; + int32 gpu = 16; + string scheduler = 17; + MountConf mount = 18; + string pull_secret = 19; + //string log_collector = 10; // XXX +} + +message CreateStudyRequest { + StudyConfig study_config = 1; +} + +message CreateStudyReply { + string study_id = 1; +} + +message StopStudyRequest { + string study_id = 1; +} + +message StopStudyReply { +} + +message GetStudysRequest { +} + +message StudyInfo { + string study_id = 1; + string name = 2; + string owner = 3; + int32 running_trial_num = 4; + int32 completed_trial_num = 5; +} + +message GetStudysReply { + repeated StudyInfo study_infos= 1; +} + +message SuggestTrialsRequest { + string study_id = 1; + string suggest_algorithm = 2; + StudyConfig configs = 3; +} + +message SuggestTrialsReply { + repeated Trial trials = 1; + bool completed = 2; +} + +message CompleteTrialRequest { + string worker_id = 1; + bool is_complete = 2; +} + +message CompleteTrialReply { +} + +message ShouldTrialStopRequest { + string study_id = 1; + string autostop_algorithm = 2; +} + +message ShouldTrialStopReply { + repeated Trial trials = 1; + repeated string worker_ids = 2; +} + +message GetObjectValueRequest { + string worker_id = 1; +} + +message GetObjectValueReply { + repeated Trial trials = 1; +} + +message AddMeasurementToTrialsRequest { + string study_id = 1; + // metrics can be a json string + string metrics = 2; +} + +message AddMeasurementToTrialsReply { +} + +message InitializeSuggestServiceRequest { + string study_id = 1; + string suggest_algorithm = 2; + repeated SuggestionParameter suggestion_parameters = 3; + StudyConfig configs = 4; +} + +message InitializeSuggestServiceReply { +} + +message GenerateTrialsRequest { + string study_id = 1; + StudyConfig configs = 2; + repeated Trial completed_trials = 3; + repeated Trial running_trials = 4; +} + +message GenerateTrialsReply { + repeated Trial trials = 1; + bool completed = 2; +} + +message SetSuggestionParametersRequest { + string study_id = 1; + repeated SuggestionParameter suggestion_parameters =2; + StudyConfig configs = 3; +} + +message SetSuggestionParametersReply { +} + +message StopSuggestionRequest { + string study_id = 1; +} + +message StopSuggestionReply { +} diff --git a/api/build.sh b/api/build.sh new file mode 100755 index 00000000000..55f520abfff --- /dev/null +++ b/api/build.sh @@ -0,0 +1 @@ +docker run -it --rm -v $PWD:$(pwd) -w $(pwd) znly/protoc --go_out=plugins=grpc:. -I. api.proto diff --git a/build.sh b/build.sh new file mode 100755 index 00000000000..0581acc1790 --- /dev/null +++ b/build.sh @@ -0,0 +1,15 @@ +#/bin/bash +set -x +set -e +PREFIX="katib/" +docker build -t ${PREFIX}vizier-core -f manager/Dockerfile . +docker build -t ${PREFIX}suggestion-random -f suggestion/random/Dockerfile . +docker build -t ${PREFIX}suggestion-grid -f suggestion/grid/Dockerfile . +docker build -t ${PREFIX}suggestion-hyperband -f suggestion/hyperband/Dockerfile . +docker build -t ${PREFIX}dlk-manager -f vendor/github.com/osrg/dlk/build/Dockerfile vendor/github.com/osrg/dlk +docker build -t ${PREFIX}katib-frontend -f manager/modeldb/Dockerfile . +docker build -t ${PREFIX}katib-cli -f cli/Dockerfile . +mkdir -p bin +docker run --name katib-cli -itd ${PREFIX}katib-cli sh +docker cp katib-cli:/go/src/github.com/mlkube/katib/cli/katib-cli bin/katib-cli +docker rm -f katib-cli diff --git a/cli/Dockerfile b/cli/Dockerfile new file mode 100644 index 00000000000..1c419756c9c --- /dev/null +++ b/cli/Dockerfile @@ -0,0 +1,22 @@ +FROM golang +RUN : && \ + go get k8s.io/api/apps/v1beta1 && \ + go get k8s.io/api/core/v1 && \ + go get k8s.io/apimachinery/pkg/apis/meta/v1 && \ + go get k8s.io/client-go/kubernetes && \ + go get k8s.io/client-go/tools/clientcmd && \ + go get k8s.io/client-go/util/homedir && \ + go get k8s.io/client-go/util/retry && \ + go get k8s.io/client-go/rest && \ + go get k8s.io/apimachinery/pkg/runtime/serializer && \ + go get database/sql && \ + go get github.com/golang/protobuf/jsonpb && \ + go get github.com/go-sql-driver/mysql && \ + go get github.com/mattn/go-sqlite3 && \ + go get google.golang.org/grpc && \ + go get gopkg.in/yaml.v2 && \ + : +ADD api $GOPATH/src/github.com/mlkube/katib/api +ADD cli $GOPATH/src/github.com/mlkube/katib/cli +WORKDIR $GOPATH/src/github.com/mlkube/katib/cli +RUN go build -o katib-cli diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 00000000000..77032f9bf03 --- /dev/null +++ b/cli/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "context" + "flag" + "fmt" + yaml "gopkg.in/yaml.v2" + "io/ioutil" + "log" + "reflect" + "strings" + + "google.golang.org/grpc" + + pb "github.com/mlkube/katib/api" +) + +var server = flag.String("s", "127.0.0.1:6789", "server address") +var confPath = flag.String("f", "", "config file path") + +// var verbose = flag.Bool("v", false, "verbose output") + +type ManagerAPI struct { + StudyConf *pb.StudyConfig +} + +func (m *ManagerAPI) Createstudy(conn *grpc.ClientConn, args []string) { + log.Printf("req Createstudy\n") + c := pb.NewManagerClient(conn) + req := &pb.CreateStudyRequest{StudyConfig: m.StudyConf} + r, err := c.CreateStudy(context.Background(), req) + if err != nil { + log.Fatalf("CreateStudy failed: %v", err) + } + log.Printf("CreateStudy: %v", r) +} + +func (m *ManagerAPI) Stopstudy(conn *grpc.ClientConn, args []string) { + log.Printf("req Stopstudy\n") + c := pb.NewManagerClient(conn) + req := &pb.StopStudyRequest{StudyId: args[1]} + r, err := c.StopStudy(context.Background(), req) + if err != nil { + log.Fatalf("StopStudy failed: %v", err) + } + log.Printf("StopStudy: %v", r) +} + +func (m *ManagerAPI) Getstudies(conn *grpc.ClientConn, args []string) { + c := pb.NewManagerClient(conn) + req := &pb.GetStudysRequest{} + r, err := c.GetStudys(context.Background(), req) + if err != nil { + log.Fatalf("GetStudy failed: %v", err) + } + fmt.Printf("StudyID \tName\tOwner\tRunningTrial\tCompletedTrial\n") + for _, si := range r.StudyInfos { + fmt.Printf("%v\t%v\t%v\t%v\t%v\n", si.StudyId, si.Name, si.Owner, si.RunningTrialNum, si.CompletedTrialNum) + } +} +func main() { + flag.Parse() + + if *confPath == "" && flag.Arg(0) == "Createstudy" { + log.Fatalf("Missing -f option") + } + + log.Printf("connecting %s", *server) + conn, err := grpc.Dial(*server, grpc.WithInsecure()) + if err != nil { + log.Fatalf("could not connect: %v", err) + } + defer conn.Close() + var sc pb.StudyConfig + var m ManagerAPI + if *confPath != "" { + buf, _ := ioutil.ReadFile(*confPath) + err = yaml.Unmarshal(buf, &sc) + log.Printf("study conf%v\n", sc) + m = ManagerAPI{StudyConf: &sc} + } + method, ok := reflect.TypeOf(&m).MethodByName( + string(strings.Title(flag.Arg(0)))) + if !ok { + log.Fatalf("Method not found: %s", flag.Arg(0)) + } + ma := []reflect.Value{ + reflect.ValueOf(&m), + reflect.ValueOf(conn), + reflect.ValueOf(flag.Args()), + } + + method.Func.Call(ma) +} diff --git a/conf/grid.yml b/conf/grid.yml new file mode 100644 index 00000000000..8bcbe24a909 --- /dev/null +++ b/conf/grid.yml @@ -0,0 +1,46 @@ +name: cifer10-grid +owner: root +optimizationtype: 2 +suggestalgorithm: grid +autostopalgorithm: median +objectivevaluename: Validation-accuracy +image: mxnet/python:gpu +suggestionparameters: + - + name: DefaultGrid + value: 3 + - + name: MaxParrallel + value: 6 + - + name: --lr + value: 2 +gpu: 1 +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 + - --num-epochs=20 + - --gpus=0 +metrics: + - accuracy +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 diff --git a/conf/hyperband.yml b/conf/hyperband.yml new file mode 100644 index 00000000000..d2f4b26f12e --- /dev/null +++ b/conf/hyperband.yml @@ -0,0 +1,57 @@ +name: cifar10-hyb +owner: root +optimizationtype: 2 +suggestalgorithm: hyperband +autostopalgorithm: median +image: mxnet/python:gpu +gpu: 1 +suggestionparameters: + - + name: Eta + value: 3 + - + name: R + value: 20 + - + name: ResourceName + value: --num-epochs +objectivevaluename: Validation-accuracy +metrics: + - accuracy +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 + - --gpus=0 +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 + - + name: --max-random-l + parametertype: 2 + feasible: + min: 25 + max: 75 + - + name: --num-epochs + parametertype: 2 + feasible: + min: 3 + max: 3 diff --git a/conf/opennmt.yml b/conf/opennmt.yml new file mode 100644 index 00000000000..d7f4d81a365 --- /dev/null +++ b/conf/opennmt.yml @@ -0,0 +1,90 @@ +name: opennmt-en-vi +owner: root +optimizationtype: 1 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: Validation_perplexity +image: yujioshima/opennmt-py +suggestionparameters: + - + name: SuggestionNum + value: 10 + - + name: MaxParallel + value: 3 +mount: + pvc: nfs + path: /nfs-mnt +gpu: 1 +command: + - bash + - run_iwslt15_en_vi.sh + - -gpuid=0 + - -data=/nfs-mnt/learndatas/iwslt15_en_vi/ + - -save_model=/nfs-mnt/logs/{{STUDY_ID}}_{{TRIAL_ID}}/en-vi +metrics: + - Train_perplexity + - Validation_accuracy + - BLEU +parameterconfigs: + configs: + - + name: -layers + parametertype: 2 + feasible: + min: 3 + max: 7 + - + name: -rnn_size + parametertype: 2 + feasible: + min: 512 + max: 512 + - + name: -word_vec_size + parametertype: 2 + feasible: + min: 200 + max: 600 + - + name: -epochs + parametertype: 2 + feasible: + min: 1 + max: 3 + - + name: -dropout + parametertype: 1 + feasible: + min: 0.01 + max: 0.3 + - + name: -warmup_steps + parametertype: 2 + feasible: + min: 5000 + max: 15000 + - + name: -learning_rate + parametertype: 1 + feasible: + min: 0.01 + max: 0.4 + - + name: -adam_beta2 + parametertype: 1 + feasible: + min: 0.5 + max: 0.99 + - + name: -batch_size + parametertype: 2 + feasible: + min: 80 + max: 80 + - + name: -start_decay_at + parametertype: 2 + feasible: + min: 5 + max: 45 diff --git a/conf/random-cpu.yml b/conf/random-cpu.yml new file mode 100644 index 00000000000..7bef18a0a88 --- /dev/null +++ b/conf/random-cpu.yml @@ -0,0 +1,53 @@ +name: cifer10 +owner: root +optimizationtype: 2 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: Validation-accuracy +scheduler: default-scheduler +image: mxnet/python +suggestionparameters: + - + name: SuggestionNum + value: 2 + - + name: MaxParallel + value: 2 +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 +metrics: + - accuracy +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 + - + name: --max-random-l + parametertype: 2 + feasible: + min: 25 + max: 75 + - + name: --num-epochs + parametertype: 2 + feasible: + min: 3 + max: 3 diff --git a/conf/random-pv.yml b/conf/random-pv.yml new file mode 100644 index 00000000000..311ff373614 --- /dev/null +++ b/conf/random-pv.yml @@ -0,0 +1,58 @@ +name: cifer10-pv-test +owner: root +optimizationtype: 2 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: Validation-accuracy +scheduler: default-scheduler +image: mxnet/python:gpu +mount: + pvc: nfs + path: /nfs-mnt +gpu: 2 +suggestionparameters: + - + name: SuggestionNum + value: 2 + - + name: MaxParallel + value: 2 +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 + - --gpus=0,1 +metrics: + - accuracy +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 + - + name: --max-random-l + parametertype: 2 + feasible: + min: 25 + max: 75 + - + name: --num-epochs + parametertype: 2 + feasible: + min: 3 + max: 3 diff --git a/conf/random.yml b/conf/random.yml new file mode 100644 index 00000000000..8efafdf9e26 --- /dev/null +++ b/conf/random.yml @@ -0,0 +1,55 @@ +name: cifer10 +owner: root +optimizationtype: 2 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: Validation-accuracy +scheduler: default-scheduler +image: mxnet/python:gpu +gpu: 2 +suggestionparameters: + - + name: SuggestionNum + value: 2 + - + name: MaxParallel + value: 2 +command: + - python + - /mxnet/example/image-classification/train_cifar10.py + - --batch-size=512 + - --gpus=0,1 +metrics: + - accuracy +parameterconfigs: + configs: + - + name: --lr + parametertype: 1 + feasible: + min: 0.03 + max: 0.07 + - + name: --lr-factor + parametertype: 1 + feasible: + min: 0.05 + max: 0.2 + - + name: --max-random-h + parametertype: 2 + feasible: + min: 26 + max: 46 + - + name: --max-random-l + parametertype: 2 + feasible: + min: 25 + max: 75 + - + name: --num-epochs + parametertype: 2 + feasible: + min: 3 + max: 3 diff --git a/conf/tf-nmt.yml b/conf/tf-nmt.yml new file mode 100644 index 00000000000..dc2b91bedbc --- /dev/null +++ b/conf/tf-nmt.yml @@ -0,0 +1,105 @@ +name: tf-nmt +owner: root +optimizationtype: 1 +suggestalgorithm: random +autostopalgorithm: median +objectivevaluename: test_ppl +metrics: + - ppl + - bleu_dev + - bleu_test +image: yujioshima/tf-nmt:latest-gpu +scheduler: default-scheduler +mount: + pvc: nfs + path: /nfs-mnt +suggestionparameters: + - + name: SuggestionNum + value: 10 + - + name: MaxParallel + value: 6 +gpu: 1 +command: + - python + - -m + - nmt.nmt + - --src=vi + - --tgt=en + - --out_dir=/nfs-mnt/logs/{{STUDY_ID}}_{{TRIAL_ID}} + - --vocab_prefix=/nfs-mnt/learndatas/iwslt15_en_vi/vocab + - --train_prefix=/nfs-mnt/learndatas/iwslt15_en_vi/train + - --dev_prefix=/nfs-mnt/learndatas/iwslt15_en_vi/tst2012 + - --test_prefix=/nfs-mnt/learndatas/iwslt15_en_vi/tst2013 + - --attention_architecture=standard + - --attention=normed_bahdanau + - --batch_size=128 + - --colocate_gradients_with_ops=true + - --eos= + - --forget_bias=1.0 + - --init_weight=0.1 + - --learning_rate=1.0 + - --max_gradient_norm=5.0 + - --metrics=bleu + - --share_vocab=false + - --num_buckets=5 + - --optimizer=sgd + - --sos= + - --steps_per_stats=100 + - --time_major=true + - --unit_type=lstm + - --src_max_len=50 + - --tgt_max_len=50 + - --infer_batch_size=32 +parameterconfigs: + configs: + - + name: --num_train_steps + parametertype: 2 + feasible: + min: 1000 + max: 1000 + - + name: --dropout + parametertype: 1 + feasible: + min: 0.1 + max: 0.3 + - + name: --beam_width + parametertype: 2 + feasible: + min: 5 + max: 15 + - + name: --num_units + parametertype: 2 + feasible: + min: 256 + max: 1026 + - + name: --attention + parametertype: 4 + feasible: + list: + - luong + - scaled_luong + - bahdanau + - normed_bahdanau + - + name: --decay_scheme + parametertype: 4 + feasible: + list: + - luong234 + - luong5 + - luong10 + - + name: --encoder_type + parametertype: 4 + feasible: + list: + - bi + - uni + diff --git a/db/db_init.go b/db/db_init.go new file mode 100644 index 00000000000..1f71067e28a --- /dev/null +++ b/db/db_init.go @@ -0,0 +1,71 @@ +package db + +import ( + "log" +) + +func (d *db_conn) DB_Init() { + db := d.db + _, err := db.Exec("CREATE TABLE IF NOT EXISTS studies" + + "(id CHAR(16) PRIMARY KEY, " + + "name VARCHAR(255), " + + "owner VARCHAR(255), " + + "optimization_type TINYINT, " + + "optimization_goal DOUBLE, " + + "parameter_configs TEXT, " + + "suggest_algo VARCHAR(255), " + + "autostop_algo VARCHAR(255), " + + "study_task_name VARCHAR(255), " + + "suggestion_parameters TEXT, " + + "tags TEXT, " + + "objective_value_name VARCHAR(255), " + + "metrics TEXT, " + + "image VARCHAR(255), " + + "command TEXT, " + + "gpu INT, " + + "scheduler VARCHAR(255), " + + "mount TEXT, " + + "pull_secret TEXT)") + if err != nil { + log.Fatalf("Error creating studies table: %v", err) + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS study_permissions" + + "(study_id CHAR(16) NOT NULL, " + + "access_permission VARCHAR(255), " + + "PRIMARY KEY (study_id, access_permission))") + if err != nil { + log.Fatalf("Error creating study_permissions table: %v", err) + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS trials" + + "(id CHAR(16) PRIMARY KEY, " + + "study_id CHAR(16), " + + "parameters TEXT, " + + "status TINYINT, " + + "objective_value VARCHAR(255), " + + "tags TEXT, " + + "FOREIGN KEY(study_id) REFERENCES studies(id))") + if err != nil { + log.Fatalf("Error creating trials table: %v", err) + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS trial_logs" + + "(trial_id CHAR(16) NOT NULL, " + + "time DATETIME(6), " + + "value TEXT, " + + "PRIMARY KEY (trial_id, time))") + // We can have "id INT AUTO_INCREMENT PRIMARY KEY" instead. + if err != nil { + log.Fatalf("Error creating trial_logs table: %v", err) + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS workers" + + "(id CHAR(16) PRIMARY KEY, " + + "trial_id CHAR(16), " + + "status TINYINT, " + + "FOREIGN KEY(trial_id) REFERENCES trials(id))") + if err != nil { + log.Fatalf("Error creating workers table: %v", err) + } +} diff --git a/db/interface.go b/db/interface.go new file mode 100644 index 00000000000..162805b29ad --- /dev/null +++ b/db/interface.go @@ -0,0 +1,474 @@ +package db + +import ( + crand "crypto/rand" + "database/sql" + "errors" + "fmt" + "github.com/golang/protobuf/jsonpb" + "log" + "math/big" + "math/rand" + "strings" + "time" + + api "github.com/mlkube/katib/api" + + _ "github.com/go-sql-driver/mysql" +) + +const ( + db_driver = "mysql" + db_name = "root:test@tcp(vizier-db:3306)/vizier" + mysql_time_fmt = "2006-01-02 15:04:05.999999" +) + +type GetTrialLogOpts struct { +} + +type TrialLog struct { + Time string + Value string +} + +type VizierDBInterface interface { + DB_Init() + GetStudyConfig(string) (*api.StudyConfig, error) + GetStudyList() ([]string, error) + CreateStudy(*api.StudyConfig) (string, error) + DeleteStudy(string) error + + GetTrial(string) (*api.Trial, error) + GetTrialStatus(string) (api.TrialState, error) + GetTrialList(string) ([]*api.Trial, error) + CreateTrial(*api.Trial) error + UpdateTrial(string, api.TrialState) error + GetTrialLogs(string, *GetTrialLogOpts) ([]*TrialLog, error) + GetTrialTimestamp(string) (*time.Time, error) + StoreTrialLogs(string, []string) error + DeleteTrial(string) error +} + +type db_conn struct { + db *sql.DB +} + +var rs1Letters = []rune("abcdefghijklmnopqrstuvwxyz") + +func NewWithSqlConn(db *sql.DB) VizierDBInterface { + d := new(db_conn) + d.db = db + seed, err := crand.Int(crand.Reader, big.NewInt(1<<63-1)) + if err != nil { + log.Fatalf("RNG initialization failed: %v", err) + } + // We can do the following instead, but it creates a locking issue + //d.rng = rand.New(rand.NewSource(seed.Int64())) + rand.Seed(seed.Int64()) + + return d +} + +func New() VizierDBInterface { + db, err := sql.Open(db_driver, db_name) + if err != nil { + log.Fatalf("DB open failed: %v", err) + } + return NewWithSqlConn(db) +} + +func generate_randid() string { + // UUID isn't quite handy in the Go world + id_ := make([]byte, 8) + _, err := rand.Read(id_) + if err != nil { + log.Printf("Error reading random: %v", err) + return "" + } + return string(rs1Letters[rand.Intn(len(rs1Letters))]) + fmt.Sprintf("%016x", id_)[1:] +} + +func (d *db_conn) GetStudyConfig(id string) (*api.StudyConfig, error) { + row := d.db.QueryRow("SELECT * FROM studies WHERE id = ?", id) + + study := new(api.StudyConfig) + var dummy_id, configs, suggestion_parameters, tags, metrics, command, mconf string + err := row.Scan(&dummy_id, + &study.Name, + &study.Owner, + &study.OptimizationType, + &study.OptimizationGoal, + &configs, + &study.SuggestAlgorithm, + &study.AutostopAlgorithm, + &study.StudyTaskName, + &suggestion_parameters, + &tags, + &study.ObjectiveValueName, + &metrics, + &study.Image, + &command, + &study.Gpu, + &study.Scheduler, + &mconf, + &study.PullSecret, + ) + if err != nil { + return nil, err + } + study.ParameterConfigs = new(api.StudyConfig_ParameterConfigs) + err = jsonpb.UnmarshalString(configs, study.ParameterConfigs) + if err != nil { + return nil, err + } + + var sp_array []string + if len(suggestion_parameters) > 0 { + sp_array = strings.Split(suggestion_parameters, ",\n") + } + study.SuggestionParameters = make([]*api.SuggestionParameter, len(sp_array)) + for i, j := range sp_array { + sp := new(api.SuggestionParameter) + err = jsonpb.UnmarshalString(j, sp) + if err != nil { + log.Printf("err unmarshal %s", j) + return nil, err + } + study.SuggestionParameters[i] = sp + } + + var tags_array []string + if len(tags) > 0 { + tags_array = strings.Split(tags, ",\n") + } + study.Tags = make([]*api.Tag, len(tags_array)) + for i, j := range tags_array { + tag := new(api.Tag) + err = jsonpb.UnmarshalString(j, tag) + if err != nil { + log.Printf("err unmarshal %s", j) + return nil, err + } + study.Tags[i] = tag + } + + study.Mount = new(api.MountConf) + if mconf != "" { + err = jsonpb.UnmarshalString(mconf, study.Mount) + if err != nil { + return nil, err + } + } + + study.Metrics = strings.Split(metrics, ",\n") + study.Command = strings.Split(command, ",\n") + return study, nil +} + +func (d *db_conn) GetStudyList() ([]string, error) { + rows, err := d.db.Query("SELECT id FROM studies") + if err != nil { + return nil, err + } + + defer rows.Close() + var result []string + for rows.Next() { + var id string + err = rows.Scan(&id) + if err != nil { + log.Printf("err scanning studies.id: %v", err) + continue + } + result = append(result, id) + } + return result, nil +} + +func (d *db_conn) CreateStudy(in *api.StudyConfig) (string, error) { + configs, err := (&jsonpb.Marshaler{}).MarshalToString(in.ParameterConfigs) + if err != nil { + log.Fatalf("Error marshaling configs: %v", err) + } + + suggestion_parameters := make([]string, len(in.SuggestionParameters)) + for i, elem := range in.SuggestionParameters { + suggestion_parameters[i], err = (&jsonpb.Marshaler{}).MarshalToString(elem) + if err != nil { + log.Printf("Error marshalling %v: %v", elem, err) + } + } + var mconf string = "" + if in.Mount != nil { + mconf, err = (&jsonpb.Marshaler{}).MarshalToString(in.Mount) + if err != nil { + log.Fatalf("Error marshaling mount configs: %v", err) + } + } + + tags := make([]string, len(in.Tags)) + for i, elem := range in.Tags { + tags[i], err = (&jsonpb.Marshaler{}).MarshalToString(elem) + if err != nil { + log.Printf("Error marshalling %v: %v", elem, err) + continue + } + } + + var study_id string + i := 3 + for true { + study_id = generate_randid() + _, err := d.db.Exec( + "INSERT INTO studies VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + study_id, + in.Name, + in.Owner, + in.OptimizationType, + in.OptimizationGoal, + configs, + in.SuggestAlgorithm, + in.AutostopAlgorithm, + in.StudyTaskName, + strings.Join(suggestion_parameters, ",\n"), + strings.Join(tags, ",\n"), + in.ObjectiveValueName, + strings.Join(in.Metrics, ",\n"), + in.Image, + strings.Join(in.Command, ",\n"), + in.Gpu, + in.Scheduler, + mconf, + in.PullSecret, + ) + if err == nil { + break + } else { + errmsg := strings.ToLower(err.Error()) + if strings.Contains(errmsg, "unique") || strings.Contains(errmsg, "duplicate") { + i-- + if i > 0 { + continue + } + } + } + return "", err + } + for _, perm := range in.AccessPermissions { + _, err := d.db.Exec( + "INSERT INTO study_permissions (study_id, access_permission) "+ + "VALUES (?, ?)", + study_id, perm) + if err != nil { + log.Printf("Error storing permission (%s, %s): %v", + study_id, perm, err) + } + } + + return study_id, nil +} + +func (d *db_conn) DeleteStudy(id string) error { + _, err := d.db.Exec("DELETE FROM studies WHERE id = ?", id) + return err +} + +func (d *db_conn) getTrials(trial_id string, study_id string) ([]*api.Trial, error) { + var rows *sql.Rows + var err error + + if trial_id != "" { + rows, err = d.db.Query("SELECT * FROM trials WHERE id = ?", trial_id) + } else if study_id != "" { + rows, err = d.db.Query("SELECT * FROM trials WHERE study_id = ?", study_id) + } else { + return nil, errors.New("trial_id or study_id must be set") + } + + if err != nil { + return nil, err + } + + var result []*api.Trial + for rows.Next() { + trial := new(api.Trial) + + var parameters, tags string + err := rows.Scan(&trial.TrialId, + &trial.StudyId, + ¶meters, + &trial.Status, + &trial.ObjectiveValue, + &tags, + ) + if err != nil { + return nil, err + } + // XXX need to unmarshall parameters & tags + result = append(result, trial) + } + + return result, nil +} + +func (d *db_conn) GetTrial(id string) (*api.Trial, error) { + trials, err := d.getTrials(id, "") + if err != nil { + return nil, err + } + + if len(trials) > 1 { + return nil, errors.New("multiple trials found") + } else if len(trials) == 0 { + return nil, errors.New("trials not found") + } + + return trials[0], nil +} + +func (d *db_conn) GetTrialStatus(id string) (api.TrialState, error) { + status := api.TrialState_ERROR + + row := d.db.QueryRow("SELECT status FROM trials WHERE id = ?", id) + err := row.Scan(&status) + if err != nil { + return status, err + } + return status, nil +} + +func (d *db_conn) GetTrialList(id string) ([]*api.Trial, error) { + trials, err := d.getTrials("", id) + + return trials, err +} + +func (d *db_conn) CreateTrial(trial *api.Trial) error { + // This function sets trial.id, unlike old dbInsertTrials(). + // Users should not overwrite trial.id + var err, lastErr error + + params := make([]string, len(trial.ParameterSet)) + for i, elem := range trial.ParameterSet { + params[i], err = (&jsonpb.Marshaler{}).MarshalToString(elem) + if err != nil { + log.Printf("Error marshalling trial.ParameterSet %v: %v", + elem, err) + lastErr = err + } + } + tags := make([]string, len(trial.Tags)) + for i := range tags { + tags[i], err = (&jsonpb.Marshaler{}).MarshalToString(trial.Tags[i]) + if err != nil { + log.Printf("Error marshalling trial.Tags %v: %v", + trial.Tags[i], err) + lastErr = err + } + } + + var trial_id string + i := 3 + for true { + trial_id = generate_randid() + _, err = d.db.Exec("INSERT INTO trials VALUES (?, ?, ?, ?, ?, ?)", + trial_id, trial.StudyId, strings.Join(params, ",\n"), + trial.Status, trial.ObjectiveValue, strings.Join(tags, ",\n")) + if err == nil { + trial.TrialId = trial_id + break + } else { + errmsg := strings.ToLower(err.Error()) + if strings.Contains(errmsg, "unique") || strings.Contains(errmsg, "duplicate") { + i-- + if i > 0 { + continue + } + } + } + return err + } + return lastErr +} + +func (d *db_conn) UpdateTrial(id string, newstatus api.TrialState) error { + _, err := d.db.Exec("UPDATE trials SET status = ? WHERE id = ?", newstatus, id) + return err +} + +func (d *db_conn) GetTrialLogs(id string, opts *GetTrialLogOpts) ([]*TrialLog, error) { + // TODO: opts not implemented + rows, err := d.db.Query("SELECT (time, value) FROM trial_logs WHERE trial_id = ? ORDER BY time", id) + if err != nil { + return nil, err + } + + var result []*TrialLog + for rows.Next() { + log1 := new(TrialLog) + + err := rows.Scan(&((*log1).Time), &((*log1).Value)) + if err != nil { + log.Printf("Error scanning log: %v", err) + continue + } + result = append(result, log1) + } + return result, nil +} + +func (d *db_conn) GetTrialTimestamp(id string) (*time.Time, error) { + var last_timestamp string + + row := d.db.QueryRow("SELECT time FROM trial_logs WHERE trial_id = ? ORDER BY time DESC LIMIT 1", id) + err := row.Scan(&last_timestamp) + switch { + case err == sql.ErrNoRows: + return nil, nil + case err != nil: + return nil, err + default: + mt, err := time.Parse(mysql_time_fmt, last_timestamp) + if err != nil { + log.Printf("Error parsing time in log %s: %v", + last_timestamp, err) + return nil, err + } + return &mt, nil + } +} + +func (d *db_conn) StoreTrialLogs(trial_id string, logs []string) error { + var lasterr error + + for _, logline := range logs { + if logline == "" { + continue + } + ls := strings.SplitN(logline, " ", 2) + if len(ls) != 2 { + log.Printf("Error parsing log: %s", logline) + lasterr = errors.New("Error parsing log") + continue + } + t, err := time.Parse(time.RFC3339Nano, ls[0]) + if err != nil { + log.Printf("Error parsing time %s: %v", ls[0], err) + lasterr = err + continue + } + // use UTC as mysql DATETIME lacks timezone + _, err = d.db.Exec("INSERT INTO trial_logs VALUES (?, ?, ?)", + trial_id, t.UTC().Format("2006-01-02 15:04:05.999999"), ls[1]) + if err != nil { + log.Printf("Error storing log %s: %v", logline, err) + lasterr = err + } + } + return lasterr +} + +func (d *db_conn) DeleteTrial(id string) error { + _, err := d.db.Exec("DELETE FROM trials WHERE id = ?", id) + return err +} diff --git a/db/interface_test.go b/db/interface_test.go new file mode 100644 index 00000000000..ed6bdac1112 --- /dev/null +++ b/db/interface_test.go @@ -0,0 +1,79 @@ +// This test assumes mysql listening on localhost:3306, which can be +// prepared by the following: +// docker run -e MYSQL_ROOT_PASSWORD=test123 -e MYSQL_DATABASE=vizier -p 3306:3306 mysql + +package db + +import ( + "database/sql" + "fmt" + "github.com/golang/protobuf/jsonpb" + "math/rand" + "os" + "testing" + + api "github.com/mlkube/katib/api" + + _ "github.com/go-sql-driver/mysql" +) + +var db_interface VizierDBInterface + +func TestMain(m *testing.M) { + db, err := sql.Open("mysql", "root:test123@tcp(localhost:3306)/vizier") + if err != nil { + fmt.Printf("error opening db: %v\n", err) + os.Exit(1) + } + db_interface = NewWithSqlConn(db) + db_interface.DB_Init() + + os.Exit(m.Run()) +} + +func TestGetStudyConfig(t *testing.T) { + var in api.StudyConfig + in.ParameterConfigs = new(api.StudyConfig_ParameterConfigs) + //err := jsonpb.UnmarshalString("{}", &in) + err := jsonpb.UnmarshalString(`{"configs": [{"name": "-abc"}]}`, in.ParameterConfigs) + if err != nil { + t.Errorf("err %v", err) + } + + id, err := db_interface.CreateStudy(&in) + if err != nil { + t.Fatalf("CreateStudy error %v", err) + } + study, err := db_interface.GetStudyConfig(id) + if err != nil { + t.Fatalf("GetStudyConfig failed: %v", err) + } + fmt.Printf("%v", study) + // TODO: check study data +} + +func TestCreateStudyIdGeneration(t *testing.T) { + var in api.StudyConfig + in.ParameterConfigs = new(api.StudyConfig_ParameterConfigs) + + var ids []string + for i := 0; i < 4; i++ { + rand.Seed(1) + id, err := db_interface.CreateStudy(&in) + if i < 3 { + if err != nil { + t.Errorf("CreateStudy error %v", err) + } + ids = append(ids, id) + } else if err == nil { + t.Fatal("Expected error but succeeded") + } + t.Logf("id gen %d %s %v\n", i, id, err) + } + for _, id := range ids { + err := db_interface.DeleteStudy(id) + if err != nil { + t.Errorf("DeleteStudy error %v", err) + } + } +} diff --git a/db/test/test.go b/db/test/test.go new file mode 100644 index 00000000000..bb0f12e08d2 --- /dev/null +++ b/db/test/test.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "github.com/mlkube/katib/db" + "os" +) + +func main() { + db_int := db.New() + study, err := db_int.GetStudyConfig(os.Args[1]) + if err != nil { + fmt.Printf("err: %v", err) + } else { + fmt.Printf("%v", study) + } +} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000000..5637a101426 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,11 @@ +#/bin/bash +set -x +set -e +kubectl apply -f manifests/0-namespace.yaml +kubectl apply -f manifests/modeldb/db +kubectl apply -f manifests/modeldb/backend +kubectl apply -f manifests/modeldb/frontend +kubectl apply -f manifests/dlk +kubectl apply -f manifests/vizier/db +kubectl apply -f manifests/vizier/core +kubectl apply -f manifests/vizier/suggestion/random diff --git a/earlystopping/earlyStoppingService.go b/earlystopping/earlyStoppingService.go new file mode 100644 index 00000000000..f7bf7fa7f79 --- /dev/null +++ b/earlystopping/earlyStoppingService.go @@ -0,0 +1,67 @@ +package earlystopping + +import ( + // "fmt" + "github.com/mlkube/katib/api" + "sort" + // "strconv" +) + +type EarlyStoppingService interface { + ShouldStoppingTrial(runningTrials []*api.Trial, completedTrials []*api.Trial, leastStep int) []api.Trial +} + +type MedianStoppingRule struct{} + +func NewMedianStoppingRule() *MedianStoppingRule { + m := &MedianStoppingRule{} + return m +} + +func (m *MedianStoppingRule) getMedianRunningAverage(completedTrials []api.Trial, step int) float64 { + r := []float64{} + for _, ct := range completedTrials { + if ct.Status == api.TrialState_COMPLETED { + // var ra float64 + // for s := 0; s < step; s++ { + // p, _ := strconv.ParseFloat(ct.EvalLogs[s].Value, 64) + // ra += p + // } + // ra = ra / float64(len(ct.EvalLogs)) + // r = append(r, ra) + // var p float64 + // if len(ct.EvalLogs) < step { + // p, _ = strconv.ParseFloat(ct.EvalLogs[len(ct.EvalLogs)-1].Value, 64) + // } else { + // p, _ = strconv.ParseFloat(ct.EvalLogs[step-1].Value, 64) + // } + // r = append(r, p) + } + } + if len(r) == 0 { + return 0 + } else { + sort.Float64s(r) + return r[len(r)/2] + } +} + +func (m *MedianStoppingRule) ShouldStoppingTrial(runningTrials []api.Trial, completedTrials []api.Trial, leastStep int) []api.Trial { + s_t := []api.Trial{} + for _, t := range runningTrials { + if t.Status != api.TrialState_RUNNING { + continue + } + s := len(t.EvalLogs) + if s < leastStep { + continue + } + //om := m.getMedianRunningAverage(completedTrials, s) + // v, _ := strconv.ParseFloat(t.EvalLogs[s-1].Value, 64) + // fmt.Printf("Trial %v, Current value %v Median value in step %v %v\n", t.TrialId, v, s, om) + // if v < (om - 0.03) { + // s_t = append(s_t, t) + // } + } + return s_t +} diff --git a/manager/Dockerfile b/manager/Dockerfile new file mode 100644 index 00000000000..dcb293e8085 --- /dev/null +++ b/manager/Dockerfile @@ -0,0 +1,33 @@ +FROM golang +RUN : && \ + go get k8s.io/api/apps/v1beta1 && \ + go get k8s.io/api/core/v1 && \ + go get k8s.io/apimachinery/pkg/apis/meta/v1 && \ + go get k8s.io/client-go/kubernetes && \ + go get k8s.io/client-go/tools/clientcmd && \ + go get k8s.io/client-go/util/homedir && \ + go get k8s.io/client-go/util/retry && \ + go get k8s.io/client-go/rest && \ + go get k8s.io/apimachinery/pkg/runtime/serializer && \ + go get database/sql && \ + go get github.com/golang/protobuf/jsonpb && \ + go get github.com/go-sql-driver/mysql && \ + go get github.com/mattn/go-sqlite3 && \ + go get google.golang.org/grpc && \ + go get github.com/sirupsen/logrus && \ + go get github.com/docker/docker/api/types && \ + go get github.com/docker/docker/api/types/container && \ + go get github.com/docker/docker/client && \ + : +RUN apt update && apt install -y python python-pip +RUN pip install modeldb +ADD api $GOPATH/src/github.com/mlkube/katib/api +ADD db $GOPATH/src/github.com/mlkube/katib/db +ADD manager $GOPATH/src/github.com/mlkube/katib/manager +ADD vendor $GOPATH/src/github.com/mlkube/katib/vendor +ADD earlystopping $GOPATH/src/github.com/mlkube/katib/earlystopping +ADD conf /conf +ADD manager/wrap.sh /wrap.sh +RUN chmod 755 /wrap.sh +WORKDIR $GOPATH/src/github.com/mlkube/katib/manager +RUN go build -o vizier-manager diff --git a/manager/main.go b/manager/main.go new file mode 100644 index 00000000000..3089c7ef137 --- /dev/null +++ b/manager/main.go @@ -0,0 +1,335 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "net" + "os" + "time" + + "github.com/mlkube/katib/manager/worker_interface" + dlkwif "github.com/mlkube/katib/manager/worker_interface/dlk" + k8swif "github.com/mlkube/katib/manager/worker_interface/kubernetes" + nvdwif "github.com/mlkube/katib/manager/worker_interface/nvdocker" + + tbif "github.com/mlkube/katib/manager/visualise/tensorboard" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + + vdb "github.com/mlkube/katib/db" + batchv1 "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + pb "github.com/mlkube/katib/api" +) + +const ( + k8s_namespace = "katib" + port = "0.0.0.0:6789" +) + +var init_db = flag.Bool("init", false, "Initialize DB") +var worker = flag.String("w", "kubernetes", "Worker Typw") +var dbIf vdb.VizierDBInterface + +type studyCh struct { + stopCh chan bool + addMetricsCh chan string +} +type server struct { + wIF worker_interface.WorkerInterface + StudyChList map[string]studyCh +} + +func (s *server) saveResult(study_id string) error { + var result string + c := s.wIF.GetCompletedTrials(study_id) + if len(c) == 0 { + log.Printf("Study %v has no completed Trials", study_id) + return nil + } + result += "TrialID" + for _, p := range c[0].ParameterSet { + result += fmt.Sprintf("\t%v", p.Name) + } + result += "\tObjectiveValue" + if len(c) > 0 { + if len(c[0].EvalLogs) > 0 { + for _, m := range c[0].EvalLogs[len(c[0].EvalLogs)-1].Metrics { + result += fmt.Sprintf("\t%v", m.Name) + } + } + } + result += "\tTiem_cost" + result += "\n" + + for _, ct := range c { + result += fmt.Sprintf("%v", ct.TrialId) + for _, p := range ct.ParameterSet { + result += fmt.Sprintf("\t%v", p.Value) + } + result += fmt.Sprintf("\t%v", ct.ObjectiveValue) + for _, m := range ct.EvalLogs[len(ct.EvalLogs)-1].Metrics { + result += fmt.Sprintf("\t%v", m.Value) + } + st, _ := time.Parse(time.RFC3339, ct.EvalLogs[0].Time) + et, _ := time.Parse(time.RFC3339, ct.EvalLogs[len(ct.EvalLogs)-1].Time) + result += fmt.Sprintf("\t%v", et.Sub(st)) + result += "\n" + } + ioutil.WriteFile("/conf/result", []byte(result), os.ModePerm) + return nil +} + +func (s *server) trialIteration(conf *pb.StudyConfig, study_id string, sCh studyCh) error { + defer delete(s.StudyChList, study_id) + defer s.wIF.CleanWorkers(study_id) + tm := time.NewTimer(1 * time.Second) + log.Printf("Study %v start.", study_id) + log.Printf("Study conf %v", conf) + for { + select { + case <-tm.C: + err := s.wIF.CheckRunningTrials(study_id, conf.ObjectiveValueName, conf.Metrics) + if err != nil { + return err + } + r, err := s.SuggestTrials(context.Background(), &pb.SuggestTrialsRequest{StudyId: study_id, SuggestAlgorithm: conf.SuggestAlgorithm, Configs: conf}) + if err != nil { + log.Printf("SuggestTrials failed %v", err) + return err + } + if r.Completed { + log.Printf("Study %v completed.", study_id) + //s.saveResult(study_id) + return nil + } else if len(r.Trials) > 0 { + for _, trial := range r.Trials { + trial.Status = pb.TrialState_PENDING + trial.StudyId = study_id + err = dbIf.CreateTrial(trial) + if err != nil { + log.Printf("CreateTrial failed %v", err) + return err + } + } + err = s.wIF.SpawnWorkers(r.Trials, study_id) + if err != nil { + log.Printf("SpawnWorkers failed %v", err) + return err + } + for _, t := range r.Trials { + err = tbif.SpawnTensorBoard(study_id, t.TrialId, k8s_namespace, conf.Mount) + if err != nil { + log.Printf("SpawnTB failed %v", err) + return err + } + } + } + tm.Reset(1 * time.Second) + case <-sCh.stopCh: + log.Printf("Study %v is stopped.", study_id) + for _, t := range s.wIF.GetRunningTrials(study_id) { + t.Status = pb.TrialState_KILLED + } + return nil + case m := <-sCh.addMetricsCh: + conf.Metrics = append(conf.Metrics, m) + } + } + return nil +} + +func (s *server) CreateStudy(ctx context.Context, in *pb.CreateStudyRequest) (*pb.CreateStudyReply, error) { + if in.StudyConfig.ObjectiveValueName == "" { + return &pb.CreateStudyReply{}, errors.New("Objective_Value_Name is required.") + } + + study_id, err := dbIf.CreateStudy(in.StudyConfig) + + _, err = s.InitializeSuggestService( + ctx, + &pb.InitializeSuggestServiceRequest{ + StudyId: study_id, + SuggestAlgorithm: in.StudyConfig.SuggestAlgorithm, + SuggestionParameters: in.StudyConfig.SuggestionParameters, + Configs: in.StudyConfig, + }, + ) + if err != nil { + return &pb.CreateStudyReply{}, err + } + sCh := studyCh{stopCh: make(chan bool), addMetricsCh: make(chan string)} + go s.trialIteration(in.StudyConfig, study_id, sCh) + s.StudyChList[study_id] = sCh + return &pb.CreateStudyReply{StudyId: study_id}, nil +} + +func (s *server) StopStudy(ctx context.Context, in *pb.StopStudyRequest) (*pb.StopStudyReply, error) { + sc, ok := s.StudyChList[in.StudyId] + if !ok { + return &pb.StopStudyReply{}, errors.New("Study Id not found") + } + sc.stopCh <- false + return &pb.StopStudyReply{}, nil +} + +func spawn_worker(study_task string, params string) error { + config, err := rest.InClusterConfig() + if err != nil { + return err + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return err + } + template, err := clientset.CoreV1().PodTemplates(k8s_namespace).Get(study_task, metav1.GetOptions{}) + if err != nil { + return err + } + + _, err = clientset.BatchV1().Jobs(k8s_namespace).Create(&batchv1.Job{ + Spec: batchv1.JobSpec{ + Template: template.Template, + }, + }) + + // TODO: Update worker status + return err +} + +func (s *server) GetStudys(ctx context.Context, in *pb.GetStudysRequest) (*pb.GetStudysReply, error) { + ss := make([]*pb.StudyInfo, len(s.StudyChList)) + i := 0 + for sid := range s.StudyChList { + sc, _ := dbIf.GetStudyConfig(sid) + ss[i] = &pb.StudyInfo{ + StudyId: sid, + Name: sc.Name, + Owner: sc.Owner, + RunningTrialNum: int32(len(s.wIF.GetRunningTrials(sid))), + CompletedTrialNum: int32(len(s.wIF.GetCompletedTrials(sid))), + } + i++ + } + return &pb.GetStudysReply{StudyInfos: ss}, nil +} + +func (s *server) InitializeSuggestService(ctx context.Context, in *pb.InitializeSuggestServiceRequest) (*pb.InitializeSuggestServiceReply, error) { + conn, err := grpc.Dial("vizier-suggestion-"+in.SuggestAlgorithm+":6789", grpc.WithInsecure()) + if err != nil { + log.Printf("could not connect: %v", err) + return &pb.InitializeSuggestServiceReply{}, err + } + defer conn.Close() + c := pb.NewSuggestionClient(conn) + req := &pb.SetSuggestionParametersRequest{StudyId: in.StudyId, SuggestionParameters: in.SuggestionParameters, Configs: in.Configs} + _, err = c.SetSuggestionParameters(context.Background(), req) + if err != nil { + log.Printf("Set Suggestion Parameter failed: %v", err) + } + return &pb.InitializeSuggestServiceReply{}, err +} + +func (s *server) SuggestTrials(ctx context.Context, in *pb.SuggestTrialsRequest) (*pb.SuggestTrialsReply, error) { + var suggest_algo string + + // TODO: only a few columns are needed but GetStudyConfig does a full retrieval + study, err := dbIf.GetStudyConfig(in.StudyId) + if err != nil { + return nil, err + } + + if in.SuggestAlgorithm != "" { + suggest_algo = in.SuggestAlgorithm + } else if study.SuggestAlgorithm != "" { + suggest_algo = study.SuggestAlgorithm + } else { + return &pb.SuggestTrialsReply{Completed: false}, errors.New("No suggest algorithm specified") + } + + conn, err := grpc.Dial("vizier-suggestion-"+suggest_algo+":6789", grpc.WithInsecure()) + if err != nil { + return &pb.SuggestTrialsReply{Completed: false}, err + } + + defer conn.Close() + c := pb.NewSuggestionClient(conn) + cts := s.wIF.GetCompletedTrials(in.StudyId) + rts := s.wIF.GetRunningTrials(in.StudyId) + req := &pb.GenerateTrialsRequest{StudyId: in.StudyId, Configs: in.Configs, CompletedTrials: cts, RunningTrials: rts} + r, err := c.GenerateTrials(context.Background(), req) + if err != nil { + return &pb.SuggestTrialsReply{Completed: false}, err + } + + // TODO: do async + return &pb.SuggestTrialsReply{Trials: r.Trials, Completed: r.Completed}, nil +} + +func (s *server) CompleteTrial(context.Context, *pb.CompleteTrialRequest) (*pb.CompleteTrialReply, error) { + return nil, errors.New("not implemented") +} +func (s *server) ShouldTrialStop(context.Context, *pb.ShouldTrialStopRequest) (*pb.ShouldTrialStopReply, error) { + return nil, errors.New("not implemented") +} +func (s *server) GetObjectValue(context.Context, *pb.GetObjectValueRequest) (*pb.GetObjectValueReply, error) { + return nil, errors.New("not implemented") +} + +func (s *server) AddMeasurementToTrials(context.Context, *pb.AddMeasurementToTrialsRequest) (*pb.AddMeasurementToTrialsReply, error) { + + return &pb.AddMeasurementToTrialsReply{}, nil +} + +func main() { + flag.Parse() + + // if *init_db { + var err error + dbIf = vdb.New() + + dbIf.DB_Init() + // } else { + listener, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + size := 1<<31 - 1 + s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size)) + switch *worker { + case "kubernetes": + log.Printf("Worker: kubernetes\n") + kc, err := clientcmd.BuildConfigFromFlags("", "/conf/kubeconfig") + if err != nil { + log.Fatal(err) + } + clientset, err := kubernetes.NewForConfig(kc) + if err != nil { + log.Fatal(err) + } + pb.RegisterManagerServer(s, &server{wIF: k8swif.NewKubernetesWorkerInterface(clientset, dbIf), StudyChList: make(map[string]studyCh)}) + // XXX Is this useful? + case "dlk": + log.Printf("Worker: dlk\n") + pb.RegisterManagerServer(s, &server{wIF: dlkwif.NewDlkWorkerInterface("http://dlk-manager:1323", k8s_namespace), StudyChList: make(map[string]studyCh)}) + case "nv-docker": + log.Printf("Worker: nv-docker\n") + pb.RegisterManagerServer(s, &server{wIF: nvdwif.NewNvDockerWorkerInterface(), StudyChList: make(map[string]studyCh)}) + default: + log.Fatalf("Unknown worker") + } + reflection.Register(s) + if err = s.Serve(listener); err != nil { + log.Fatalf("Failed to serve: %v", err) + } + // } +} diff --git a/manager/modeldb/Dockerfile b/manager/modeldb/Dockerfile new file mode 100644 index 00000000000..7afdd186cd8 --- /dev/null +++ b/manager/modeldb/Dockerfile @@ -0,0 +1,35 @@ +FROM node:latest + +EXPOSE 3000 + +RUN apt-get update \ + && apt-get install -y \ + g++ \ + make \ + wget \ + git \ + && apt-get clean + +WORKDIR /root + +# Install Thrift 0.10.0 +RUN wget -q http://archive.apache.org/dist/thrift/0.10.0/thrift-0.10.0.tar.gz && \ + tar -xzf thrift-0.10.0.tar.gz && \ + cd thrift-0.10.0 && \ + ./configure --without-python && \ + make && \ + ln -n ~/thrift-0.10.0/compiler/cpp/thrift /usr/local/bin/thrift + +RUN cd / && git clone https://github.com/mitdbg/modeldb + +ADD manager/modeldb/frontend /modeldb/frontend + +WORKDIR /modeldb/frontend + +RUN npm install + +RUN mkdir -p './thrift' && \ + thrift -r -out './thrift' -gen js:node '../thrift/ModelDB.thrift' && \ + npm install +ENTRYPOINT ["/modeldb/dockerbuild/wait_for_backend.sh"] +CMD ["backend"] diff --git a/manager/modeldb/Workflow.py b/manager/modeldb/Workflow.py new file mode 100644 index 00000000000..49a11a7d733 --- /dev/null +++ b/manager/modeldb/Workflow.py @@ -0,0 +1,37 @@ +from modeldb.basic.Structs import ( + Model, ModelConfig, ModelMetrics, Dataset) +from modeldb.basic.ModelDbSyncerBase import Syncer +import sys +import json +import argparse + +parser = argparse.ArgumentParser(description='model db tiny client') +parser.add_argument('request') +parser.add_argument('-s', '--server', default="modeldb-backend") +parser.add_argument('-p', '--port', default=6543) +args = parser.parse_args() + +req_j = args.request +req = json.loads(req_j) +owner = req["owner"] +study = req["study"] +train = req["train"] +modelpath = req["modelpath"] +hyp = req["hyperparameter"] +met = req["metrics"] + +syncer_obj = Syncer.create_syncer(study, owner, "", host=args.server, port=args.port) +datasets = { + "train": Dataset("", {}), + "test": Dataset("", {}), +} + +model = train +mdb_model = Model(study, model, modelpath) +model_config = ModelConfig("NN", hyp) +model_metrics = ModelMetrics(met) + +syncer_obj.sync_datasets(datasets) +syncer_obj.sync_model("train", model_config, mdb_model) +syncer_obj.sync_metrics("test", mdb_model, model_metrics) +syncer_obj.sync() diff --git a/manager/modeldb/frontend/README.md b/manager/modeldb/frontend/README.md new file mode 100644 index 00000000000..d613626fb25 --- /dev/null +++ b/manager/modeldb/frontend/README.md @@ -0,0 +1,10 @@ +## Setup + +Make sure the ModelDB server is running, following the instructions in the server/ directory. + +Start the frontend from the frontend dir with: + + $ ./start_frontend.sh + + +Visit application at http://localhost:3000/projects diff --git a/manager/modeldb/frontend/app.js b/manager/modeldb/frontend/app.js new file mode 100755 index 00000000000..30494673231 --- /dev/null +++ b/manager/modeldb/frontend/app.js @@ -0,0 +1,62 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var routes = require('./routes/index'); +var models = require('./routes/models'); +var projects = require('./routes/projects'); + +var app = express(); + +//var root_path = process.env.ROOT_PATH +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(process.env.ROOT_PATH +'/', express.static(path.join(__dirname, 'public'))); + +app.use(process.env.ROOT_PATH + '/', routes); +app.use(process.env.ROOT_PATH + '/models', models); +app.use(process.env.ROOT_PATH + '/projects', projects); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + +module.exports = app; diff --git a/manager/modeldb/frontend/package.json b/manager/modeldb/frontend/package.json new file mode 100755 index 00000000000..486e1afc616 --- /dev/null +++ b/manager/modeldb/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "modeldb-vis", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "async": "^2.1.2", + "body-parser": "~1.15.1", + "cookie-parser": "~1.4.3", + "debug": "~2.2.0", + "ejs": "~2.5.8", + "express": "~4.13.4", + "moment": "^2.18.1", + "morgan": "~1.7.0", + "serve-favicon": "~2.3.0", + "thrift": "^0.9.3", + "yargs": "^7.1.0" + }, + "main": "app.js" +} diff --git a/manager/modeldb/frontend/public/css/lib/component.css b/manager/modeldb/frontend/public/css/lib/component.css new file mode 100755 index 00000000000..93b067cb598 --- /dev/null +++ b/manager/modeldb/frontend/public/css/lib/component.css @@ -0,0 +1,727 @@ +/* General styles for the modal */ + +/* +Styles for the html/body for special modal where we want 3d effects +Note that we need a container wrapping all content on the page for the +perspective effects (not including the modals and the overlay). +*/ +.md-perspective, +.md-perspective body { + height: 100%; + overflow: hidden; +} + +.md-perspective body { + background: #222; + -webkit-perspective: 600px; + -moz-perspective: 600px; + perspective: 600px; +} + +.container2 { + background: #e74c3c; + min-height: 100%; +} + +.md-modal { + position: fixed; + top: 40%; + left: 50%; + width: 50%; + max-width: 630px; + min-width: 320px; + height: auto; + z-index: 2000; + visibility: hidden; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform: translateX(-50%) translateY(-50%); + -moz-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); +} + +.md-show { + visibility: visible; +} + +.md-overlay { + position: fixed; + width: 100%; + height: 100%; + visibility: hidden; + top: 0; + left: 0; + z-index: 1000; + opacity: 0; + background: rgba(236,240,241,0.8); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show ~ .md-overlay { + opacity: 1; + visibility: visible; +} + +/* Content styles */ +.md-content { + color: black; + background: #D9E0E8; + position: relative; + border-radius: 3px; + margin: 0 auto; +} + +.md-content h3 { + margin: 0; + padding: 0.4em; + text-align: center; + font-size: 2.4em; + font-weight: 300; + opacity: 0.8; + background: #AEBDCD; + border-radius: 3px 3px 0 0; +} + +.md-content > div p { + margin: 0; + padding: 10px 0; +} + +.md-content > div ul { + margin: 0; + /* padding: 0 0 30px 20px; */ +} + +.md-content > div ul li { + padding: 4px 7px; +} + +.md-content button { + display: block; + margin: 0 auto; + padding: 10px; +} + +/* Individual modal styles with animations/transitions */ + +/* Effect 1: Fade in and scale up */ +.md-effect-1 .md-content { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -ms-transform: scale(0.7); + transform: scale(0.7); + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-1 .md-content { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + opacity: 1; +} + +/* Effect 2: Slide from the right */ +.md-effect-2 .md-content { + -webkit-transform: translateX(20%); + -moz-transform: translateX(20%); + -ms-transform: translateX(20%); + transform: translateX(20%); + opacity: 0; + -webkit-transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9); + -moz-transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9); + transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9); +} + +.md-show.md-effect-2 .md-content { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + opacity: 1; +} + +/* Effect 3: Slide from the bottom */ +.md-effect-3 .md-content { + -webkit-transform: translateY(20%); + -moz-transform: translateY(20%); + -ms-transform: translateY(20%); + transform: translateY(20%); + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-3 .md-content { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + opacity: 1; +} + +/* Effect 4: Newspaper */ +.md-effect-4 .md-content { + -webkit-transform: scale(0) rotate(720deg); + -moz-transform: scale(0) rotate(720deg); + -ms-transform: scale(0) rotate(720deg); + transform: scale(0) rotate(720deg); + opacity: 0; +} + +.md-show.md-effect-4 ~ .md-overlay, +.md-effect-4 .md-content { + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + transition: all 0.5s; +} + +.md-show.md-effect-4 .md-content { + -webkit-transform: scale(1) rotate(0deg); + -moz-transform: scale(1) rotate(0deg); + -ms-transform: scale(1) rotate(0deg); + transform: scale(1) rotate(0deg); + opacity: 1; +} + +/* Effect 5: fall */ +.md-effect-5.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-5 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: translateZ(600px) rotateX(20deg); + -moz-transform: translateZ(600px) rotateX(20deg); + -ms-transform: translateZ(600px) rotateX(20deg); + transform: translateZ(600px) rotateX(20deg); + opacity: 0; +} + +.md-show.md-effect-5 .md-content { + -webkit-transition: all 0.3s ease-in; + -moz-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; + -webkit-transform: translateZ(0px) rotateX(0deg); + -moz-transform: translateZ(0px) rotateX(0deg); + -ms-transform: translateZ(0px) rotateX(0deg); + transform: translateZ(0px) rotateX(0deg); + opacity: 1; +} + +/* Effect 6: side fall */ +.md-effect-6.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-6 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: translate(30%) translateZ(600px) rotate(10deg); + -moz-transform: translate(30%) translateZ(600px) rotate(10deg); + -ms-transform: translate(30%) translateZ(600px) rotate(10deg); + transform: translate(30%) translateZ(600px) rotate(10deg); + opacity: 0; +} + +.md-show.md-effect-6 .md-content { + -webkit-transition: all 0.3s ease-in; + -moz-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; + -webkit-transform: translate(0%) translateZ(0) rotate(0deg); + -moz-transform: translate(0%) translateZ(0) rotate(0deg); + -ms-transform: translate(0%) translateZ(0) rotate(0deg); + transform: translate(0%) translateZ(0) rotate(0deg); + opacity: 1; +} + +/* Effect 7: slide and stick to top */ +.md-effect-7{ + top: 0; + -webkit-transform: translateX(-50%); + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + transform: translateX(-50%); +} + +.md-effect-7 .md-content { + -webkit-transform: translateY(-200%); + -moz-transform: translateY(-200%); + -ms-transform: translateY(-200%); + transform: translateY(-200%); + -webkit-transition: all .3s; + -moz-transition: all .3s; + transition: all .3s; + opacity: 0; +} + +.md-show.md-effect-7 .md-content { + -webkit-transform: translateY(0%); + -moz-transform: translateY(0%); + -ms-transform: translateY(0%); + transform: translateY(0%); + border-radius: 0 0 3px 3px; + opacity: 1; +} + +/* Effect 8: 3D flip horizontal */ +.md-effect-8.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-8 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: rotateY(-70deg); + -moz-transform: rotateY(-70deg); + -ms-transform: rotateY(-70deg); + transform: rotateY(-70deg); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + opacity: 0; +} + +.md-show.md-effect-8 .md-content { + -webkit-transform: rotateY(0deg); + -moz-transform: rotateY(0deg); + -ms-transform: rotateY(0deg); + transform: rotateY(0deg); + opacity: 1; +} + +/* Effect 9: 3D flip vertical */ +.md-effect-9.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-9 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: rotateX(-70deg); + -moz-transform: rotateX(-70deg); + -ms-transform: rotateX(-70deg); + transform: rotateX(-70deg); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + opacity: 0; +} + +.md-show.md-effect-9 .md-content { + -webkit-transform: rotateX(0deg); + -moz-transform: rotateX(0deg); + -ms-transform: rotateX(0deg); + transform: rotateX(0deg); + opacity: 1; +} + +/* Effect 10: 3D sign */ +.md-effect-10.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-10 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: rotateX(-60deg); + -moz-transform: rotateX(-60deg); + -ms-transform: rotateX(-60deg); + transform: rotateX(-60deg); + -webkit-transform-origin: 50% 0; + -moz-transform-origin: 50% 0; + transform-origin: 50% 0; + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-10 .md-content { + -webkit-transform: rotateX(0deg); + -moz-transform: rotateX(0deg); + -ms-transform: rotateX(0deg); + transform: rotateX(0deg); + opacity: 1; +} + +/* Effect 11: Super scaled */ +.md-effect-11 .md-content { + -webkit-transform: scale(2); + -moz-transform: scale(2); + -ms-transform: scale(2); + transform: scale(2); + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-11 .md-content { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + opacity: 1; +} + +/* Effect 12: Just me */ +.md-effect-12 .md-content { + -webkit-transform: scale(0.8); + -moz-transform: scale(0.8); + -ms-transform: scale(0.8); + transform: scale(0.8); + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-12 ~ .md-overlay { + background: #e74c3c; +} + +.md-effect-12 .md-content h3, +.md-effect-12 .md-content { + background: transparent; +} + +.md-show.md-effect-12 .md-content { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + opacity: 1; +} + +/* Effect 13: 3D slit */ +.md-effect-13.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-13 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: translateZ(-3000px) rotateY(90deg); + -moz-transform: translateZ(-3000px) rotateY(90deg); + -ms-transform: translateZ(-3000px) rotateY(90deg); + transform: translateZ(-3000px) rotateY(90deg); + opacity: 0; +} + +.md-show.md-effect-13 .md-content { + -webkit-animation: slit .7s forwards ease-out; + -moz-animation: slit .7s forwards ease-out; + animation: slit .7s forwards ease-out; +} + +@-webkit-keyframes slit { + 50% { -webkit-transform: translateZ(-250px) rotateY(89deg); opacity: .5; -webkit-animation-timing-function: ease-out;} + 100% { -webkit-transform: translateZ(0) rotateY(0deg); opacity: 1; } +} + +@-moz-keyframes slit { + 50% { -moz-transform: translateZ(-250px) rotateY(89deg); opacity: .5; -moz-animation-timing-function: ease-out;} + 100% { -moz-transform: translateZ(0) rotateY(0deg); opacity: 1; } +} + +@keyframes slit { + 50% { transform: translateZ(-250px) rotateY(89deg); opacity: 1; animation-timing-function: ease-in;} + 100% { transform: translateZ(0) rotateY(0deg); opacity: 1; } +} + +/* Effect 14: 3D Rotate from bottom */ +.md-effect-14.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-14 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: translateY(100%) rotateX(90deg); + -moz-transform: translateY(100%) rotateX(90deg); + -ms-transform: translateY(100%) rotateX(90deg); + transform: translateY(100%) rotateX(90deg); + -webkit-transform-origin: 0 100%; + -moz-transform-origin: 0 100%; + transform-origin: 0 100%; + opacity: 0; + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.md-show.md-effect-14 .md-content { + -webkit-transform: translateY(0%) rotateX(0deg); + -moz-transform: translateY(0%) rotateX(0deg); + -ms-transform: translateY(0%) rotateX(0deg); + transform: translateY(0%) rotateX(0deg); + opacity: 1; +} + +/* Effect 15: 3D Rotate in from left */ +.md-effect-15.md-modal { + -webkit-perspective: 1300px; + -moz-perspective: 1300px; + perspective: 1300px; +} + +.md-effect-15 .md-content { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-transform: translateZ(100px) translateX(-30%) rotateY(90deg); + -moz-transform: translateZ(100px) translateX(-30%) rotateY(90deg); + -ms-transform: translateZ(100px) translateX(-30%) rotateY(90deg); + transform: translateZ(100px) translateX(-30%) rotateY(90deg); + -webkit-transform-origin: 0 100%; + -moz-transform-origin: 0 100%; + transform-origin: 0 100%; + opacity: 0; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-15 .md-content { + -webkit-transform: translateZ(0px) translateX(0%) rotateY(0deg); + -moz-transform: translateZ(0px) translateX(0%) rotateY(0deg); + -ms-transform: translateZ(0px) translateX(0%) rotateY(0deg); + transform: translateZ(0px) translateX(0%) rotateY(0deg); + opacity: 1; +} + +/* Effect 16: Blur */ +.md-show.md-effect-16 ~ .md-overlay { + background: rgba(180,46,32,0.5); +} + +.md-show.md-effect-16 ~ .container { + -webkit-filter: blur(3px); + -moz-filter: blur(3px); + filter: blur(3px); +} + +.md-effect-16 .md-content { + -webkit-transform: translateY(-5%); + -moz-transform: translateY(-5%); + -ms-transform: translateY(-5%); + transform: translateY(-5%); + opacity: 0; +} + +.md-show.md-effect-16 ~ .container, +.md-effect-16 .md-content { + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.md-show.md-effect-16 .md-content { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + opacity: 1; +} + +/* Effect 17: Slide in from bottom with perspective on container */ +.md-show.md-effect-17 ~ .container { + height: 100%; + overflow: hidden; + -webkit-transition: -webkit-transform 0.3s; + -moz-transition: -moz-transform 0.3s; + transition: transform 0.3s; +} + +.md-show.md-effect-17 ~ .container, +.md-show.md-effect-17 ~ .md-overlay { + -webkit-transform: rotateX(-2deg); + -moz-transform: rotateX(-2deg); + -ms-transform: rotateX(-2deg); + transform: rotateX(-2deg); + -webkit-transform-origin: 50% 0%; + -moz-transform-origin: 50% 0%; + transform-origin: 50% 0%; + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + transform-style: preserve-3d; +} + +.md-effect-17 .md-content { + opacity: 0; + -webkit-transform: translateY(200%); + -moz-transform: translateY(200%); + -ms-transform: translateY(200%); + transform: translateY(200%); +} + +.md-show.md-effect-17 .md-content { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + opacity: 1; + -webkit-transition: all 0.3s 0.2s; + -moz-transition: all 0.3s 0.2s; + transition: all 0.3s 0.2s; +} + +/* Effect 18: Slide from right with perspective on container */ +.md-show.md-effect-18 ~ .container { + height: 100%; + overflow: hidden; +} + +.md-show.md-effect-18 ~ .md-overlay { + background: rgba(143,27,15,0.8); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + transition: all 0.5s; +} + +.md-show.md-effect-18 ~ .container, +.md-show.md-effect-18 ~ .md-overlay { + -webkit-transform-style: preserve-3d; + -webkit-transform-origin: 0% 50%; + -webkit-animation: rotateRightSideFirst 0.5s forwards ease-in; + -moz-transform-style: preserve-3d; + -moz-transform-origin: 0% 50%; + -moz-animation: rotateRightSideFirst 0.5s forwards ease-in; + transform-style: preserve-3d; + transform-origin: 0% 50%; + animation: rotateRightSideFirst 0.5s forwards ease-in; +} + +@-webkit-keyframes rotateRightSideFirst { + 50% { -webkit-transform: translateZ(-50px) rotateY(5deg); -webkit-animation-timing-function: ease-out; } + 100% { -webkit-transform: translateZ(-200px); } +} + +@-moz-keyframes rotateRightSideFirst { + 50% { -moz-transform: translateZ(-50px) rotateY(5deg); -moz-animation-timing-function: ease-out; } + 100% { -moz-transform: translateZ(-200px); } +} + +@keyframes rotateRightSideFirst { + 50% { transform: translateZ(-50px) rotateY(5deg); animation-timing-function: ease-out; } + 100% { transform: translateZ(-200px); } +} + +.md-effect-18 .md-content { + -webkit-transform: translateX(200%); + -moz-transform: translateX(200%); + -ms-transform: translateX(200%); + transform: translateX(200%); + opacity: 0; +} + +.md-show.md-effect-18 .md-content { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + opacity: 1; + -webkit-transition: all 0.5s 0.1s; + -moz-transition: all 0.5s 0.1s; + transition: all 0.5s 0.1s; +} + +/* Effect 19: Slip in from the top with perspective on container */ +.md-show.md-effect-19 ~ .container { + height: 100%; + overflow: hidden; +} + +.md-show.md-effect-19 ~ .md-overlay { + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + transition: all 0.5s; +} + +.md-show.md-effect-19 ~ .container, +.md-show.md-effect-19 ~ .md-overlay { + -webkit-transform-style: preserve-3d; + -webkit-transform-origin: 50% 100%; + -webkit-animation: OpenTop 0.5s forwards ease-in; + -moz-transform-style: preserve-3d; + -moz-transform-origin: 50% 100%; + -moz-animation: OpenTop 0.5s forwards ease-in; + transform-style: preserve-3d; + transform-origin: 50% 100%; + animation: OpenTop 0.5s forwards ease-in; +} + +@-webkit-keyframes OpenTop { + 50% { + -webkit-transform: rotateX(10deg); + -webkit-animation-timing-function: ease-out; + } +} + +@-moz-keyframes OpenTop { + 50% { + -moz-transform: rotateX(10deg); + -moz-animation-timing-function: ease-out; + } +} + +@keyframes OpenTop { + 50% { + transform: rotateX(10deg); + animation-timing-function: ease-out; + } +} + +.md-effect-19 .md-content { + -webkit-transform: translateY(-200%); + -moz-transform: translateY(-200%); + -ms-transform: translateY(-200%); + transform: translateY(-200%); + opacity: 0; +} + +.md-show.md-effect-19 .md-content { + -webkit-transform: translateY(0); + -moz-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + opacity: 1; + -webkit-transition: all 0.5s 0.1s; + -moz-transition: all 0.5s 0.1s; + transition: all 0.5s 0.1s; +} + +@media screen and (max-width: 32em) { + body { font-size: 75%; } +} \ No newline at end of file diff --git a/manager/modeldb/frontend/public/css/lib/pretty-json.css b/manager/modeldb/frontend/public/css/lib/pretty-json.css new file mode 100644 index 00000000000..39623d218c7 --- /dev/null +++ b/manager/modeldb/frontend/public/css/lib/pretty-json.css @@ -0,0 +1,49 @@ +/* common */ +.mark-water{ + color:#bbb; +} +/* eof common */ + +/* node */ +.node-content-wrapper{ + font-family: 'Quicksand', sans-serif; + background-color:#fff; +} +.node-content-wrapper ul{ + border-left:1px dotted #aaa; + list-style:none; + padding-left:20px; + margin:0px; +} +.node-content-wrapper ul li{ + list-style:none; + border-bottom:0; + padding-bottom:0 +} +.node-hgl-path{ + background-color:#fefbdf; +} +.node-bracket{ + font-weight:bold; + display:inline-block; + cursor:pointer; +} +.node-bracket:hover{ + color:#999; +} +/* eof node */ + +/* leaf */ +.leaft-container{ + width:100%; + max-width:300px; + height:100%; +} + +.title{ color:#ccc;} +.string{ color:#080;} +.number{ color:#ccaa00;} +.boolean{ color:#1979d3;} +.date{ color:#aa6655;} +.null{ color:#ff5050;} +/* eof leaf */ \ No newline at end of file diff --git a/manager/modeldb/frontend/public/css/lib/vis-tooltip.css b/manager/modeldb/frontend/public/css/lib/vis-tooltip.css new file mode 100644 index 00000000000..f1648bec6f7 --- /dev/null +++ b/manager/modeldb/frontend/public/css/lib/vis-tooltip.css @@ -0,0 +1,46 @@ +.vg-tooltip { + padding: 6px; + border-radius: 3px; + position: fixed; + z-index: 2000; + font-family: sans-serif; + font-size: 11px; + + /* The default look of the tooltip is the same as .light-theme + but it can be overwritten by .dark-theme */ + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid #d9d9d9; + color: black; +} +.vg-tooltip td.key, .vg-tooltip td.value { + overflow: hidden; + text-overflow: ellipsis; +} +.vg-tooltip td.key { + color: #808080; + max-width: 150px; + text-align: right; + padding-right: 1px; +} +.vg-tooltip td.value { + max-width: 200px; + text-align: left; +} + +/* Dark and light color themes */ +.vg-tooltip.dark-theme { + background-color: rgba(64, 64, 64, 0.9); + border: none; + color: white; +} +.vg-tooltip.dark-theme td.key { + color: #bfbfbf; +} +.vg-tooltip.light-theme { + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid #d9d9d9; + color: black; +} +.vg-tooltip.light-theme td.key { + color: #808080; +} \ No newline at end of file diff --git a/manager/modeldb/frontend/public/css/lib/vis.min.css b/manager/modeldb/frontend/public/css/lib/vis.min.css new file mode 100644 index 00000000000..8d0d0bde211 --- /dev/null +++ b/manager/modeldb/frontend/public/css/lib/vis.min.css @@ -0,0 +1 @@ +.vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{background:url(img/timeline/delete.png) center no-repeat;position:absolute;width:24px;height:24px;top:-4px;right:-24px;cursor:pointer}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(/images/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(/images/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(/images/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(/images/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(/images/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(/images/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(/images/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(/images/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(/images/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(/images/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(/images/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(/images/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(/images/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} diff --git a/manager/modeldb/frontend/public/css/model.css b/manager/modeldb/frontend/public/css/model.css new file mode 100644 index 00000000000..886d8a069e8 --- /dev/null +++ b/manager/modeldb/frontend/public/css/model.css @@ -0,0 +1,215 @@ +/* + * Models + */ + +.models-container { + margin: 0 10px; + height: calc(100vh - 104px); + overflow-y: auto; + transition: all 0.3s ease; +} + +.model-headings { + margin: 10px 10px 0 10px; + background-color: #AEBDCD; + padding: 5px 20px; + border: 1px solid #D9E0E8; + font-weight: bold; + border-radius: 7px 7px 0 0; + border-bottom: 0px; + overflow: hidden; +} + +.model-heading { + padding: 4px 7px; + display: inline-block; + box-sizing: border-box; + width: 190px; + position: relative; + opacity: 1; +} + +.models-container .model { + background-color: white; + padding: 10px 20px 25px 20px; + border: 1px solid #D9E0E8; + display: inline-block; + min-width: 100%; + box-sizing: border-box; + position: relative; +} + +.models-container .model-section { + width: 190px; + display: inline-block; + vertical-align: top; +} + +.models-container .model-section .popup-label { + cursor: pointer; +} + +.models-container .kv { + padding: 4px 7px; + transition: background-color 0.3s ease; + border-radius: 2px; +} + +.models-container .nkv { + padding: 4px 7px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + cursor: inherit; +} + +.model-see-more { + position: absolute; + right: 10px; + bottom: 5px; + color: #00B7FF; + font-size: 12px; + cursor: pointer; +} + +.model-additional-info { + display: none; + border-top: 1px solid #eee; + margin-top: 15px; + opacity: 0; +} + +.model-additional-info h3 { + font-size: 18px; + margin: 10px 0; + font-weight: 300; +} + +.model-additional-info .kv { + display: inline-block; + vertical-align: top; + width: 176px; +} + + +/* + * Popups + */ +.popup-container { + color: #00B7FF; + height: 14px; + margin-bottom: 2px; +} + +.popup-contents { + margin-top: 2px; + color: black; +} + +.open { + height: initial; + padding-bottom: 0; + margin-bottom: 0; +} + +/* + * Annotations + */ +.nkv.annotation { + position: relative; + padding-right: 16px; +} + +.annotation-create { + position: absolute; + top: 3px; + right: -1px; + line-height: 16px; + font-size: 15px; + cursor: pointer; + transition: all 0.3s ease; + opacity: 0.2; + font-weight: 600; + user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +.annotation-create:hover { + opacity: 1; +} + +.md-input-container { + padding: 10px; + background-color: rgba(174,189,205,0.5); +} + +.md-input { + display: inline-block; + width: calc(100% - 45px); + padding: 5px; + font-size: 15px; + font-weight: 300; + vertical-align: top; + border-radius: 5px; +} + +.md-send { + width: 20px; + display: inline-block; + padding: 5px; + vertical-align: top; + margin-left:2px; + margin-top: -1px; + border-radius: 5px; + opacity: 0.2; + cursor: pointer; + transition: all 0.3s ease; +} + +.md-send.enabled { + opacity: 0.5; +} + +.md-send.enabled:hover { + opacity: 0.8; +} + +.md-model-id { + display: inline-block; +} + +.md-header { + background-color: #2c3e50 + margin: 0; + padding: 10px; + text-align: center; + font-size: 18px; + font-weight: 300; + opacity: 0.8; + background: #2c3e50; + border-radius: 3px 3px 0 0; + color: white; +} + +.md-body { + +} + +.md-annotations { + height: 300px; + overflow-y: scroll; + padding: 5px 10px; +} + +.md-annotation { + background-color: white; + padding: 7px; + margin: 5px 2px; + border-radius: 5px; + display: inline-block; + font-size: 15px; +} + diff --git a/manager/modeldb/frontend/public/css/models.css b/manager/modeldb/frontend/public/css/models.css new file mode 100644 index 00000000000..b9c9f59a122 --- /dev/null +++ b/manager/modeldb/frontend/public/css/models.css @@ -0,0 +1,998 @@ +h1 { + font-size: 24px; + font-weight: 500; + line-height: 30px; + margin: 0 0 10px 0; +} + +h2 { + font-size: 16px; +} + +.container { + padding-left: 250px; + overflow-x: hidden; + white-space: nowrap; +} + +.tab{ +width: 100%; +display: inline-block; +vertical-align: top; +height: calc(100vh - 49px); +overflow-x: hidden; +overflow-y: scroll; +} + +/* + * Menu + */ + +.menu { + background-color: #D9E0E8; + position: fixed; + left: 0; + height: 100vh; + width: 250px; + white-space: initial; + z-index: 2; +} + +.menu-tabs-container { + border-bottom: 1px dashed #2c3e50; +} + +.next-section-container { + border-top: 1px dashed #2c3e50; +} + +.menu-tab { + padding: 10px; + cursor: pointer; + transition: all 0.3s ease-out; +} + +.menu-tab:hover { + background-color: #d0d8e2; +} + +.menu .selected { + font-weight: bold; + background-color: #AEBDCD; + transition: all 0.3s ease-out; +} + +.menu .selected-arrow { + content: ''; + position: absolute; + right: -12px; + top: 17px; + width: 0; + height: 0; + border-top: 12px solid transparent; + border-bottom: 12px solid transparent; + border-left: 12px solid #AEBDCD; + transition: all 0.1s ease; +} + +.menu-tab-label { + line-height: 40px; + vertical-align: top; + display: inline-block; + margin-left: 5px; + font-size: 16px; +} + +.menu-tab img { + height: 40px; + display:inline-block; + vertical-align: top; +} + +.menu-section-container { + padding: 10px; +} + +.filter-area, .set-headings-area { + height: 150px; + overflow-y: auto; + margin-top: 5px; + transition: all 0.5s ease; + border: 1px solid #D9E0E8; + border-bottom: none; + padding: 0 10px; +} + +.select-label { + display: inline-block; + vertical-align: top; + width: 50px; + line-height: 24px; +} + +.sort-container .select-label { + width: 50px; +} + +.select-container { + margin: 10px 0; +} + +.select-container select { + font-size: 13px; + width:175px; +} + +.group-container { + display: none; +} + +.group-container select { + width: 100%; +} + +.set-headings-container { + display: none; +} + +/* + * Project info + */ +.project-info-container, +.visualizations-container, +.parameter-explore-container{ + padding: 20px; +} + +.project-name { + float: left; +} + +.project-num-models { + float: right; + line-height: 30px; + font-size: 20px; +} + +.project-author, +.project-description { + clear: both; + font-size: 14px; +} + +/* + * Summary charts + */ +.summary-chart-container { + position: relative; + margin-bottom: 20px; +} + +.summary-chart { + margin: 20px; + overflow: hidden; + margin-bottom: 60px; +} + +.summary-chart .vega { + overflow: hidden; +} + +.summary-toggle-radio { + margin: 2px 0; +} + +.summary-toggle-radio:first-of-type { + margin-top: 8px; +} + +.summary-toggle-radio input, +.summary-toggle-radio label { + cursor: pointer; +} + +/* + * Model card + */ + +.model-card { + position: absolute; + right: -300px; + top: 0; + background-color: #D9E0E8; + opacity: 0.95; + padding: 10px; + max-height: 320px; + overflow-y: scroll; + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +.model-card .kv { + padding: 4px 7px; + cursor: move; + transition: background-color 0.3s ease; + border-radius: 2px; + width: 176px; +} + +.model-card .nkv { + padding: 4px 7px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 176px; + cursor: inherit; +} + +.model-card .nkv:hover { + background-color: #D9E0E8; +} + +.popup-container { + color: #00B7FF; + height: 14px; + margin-bottom: 2px; +} + +.model-card .popup-label { + cursor: pointer; + +} + +.popup-contents { + margin-top: 2px; + color: black; +} + +.open { + height: initial; + padding-bottom: 0; + margin-bottom: 0; +} + +/* + * Visualizations + */ + +.visualizations-container { + cursor: auto; + background-color: white; +} + +.visualizations-description { + width: 470px; + margin-bottom: 25px; + white-space: initial; +} + +.visualization-options { + display: inline-block; + margin-bottom: 20px; + vertical-align: top; +} + +.visualization-options .select-label { + width: 70px; +} + +.visualization-options .compare-button { + width: 100%; + padding: 5px; + background-color: #AEBDCD; + font-size: 14px; + color: white; + font-weight: 300; + cursor: pointer; + transition: all 0.3s ease; +} + +.compare-button:hover { + background-color: #a0b2c5; +} + +.explore-chart { + display: inline-block; + vertical-align: top; + margin-left: 40px; + max-width: 600px; + overflow-x: scroll; +} + +.explore-chart .vega { + max-height: 250px; + overflow-y: hidden; +} + +.vega-actions { + position: absolute; + display: none; +} + +.vega-actions a { + color: black; + display: inline-block; + padding: 5px; + background-color: #AEBDCD; + cursor: pointer; + transition: all 0.3s ease; + width: 200px; + color: white; + margin: 10px; + text-align: center; +} + +.vega-actions a:hover { + background-color: #a0b2c5; +} + +/* + * Models table + */ + +.data-table { + display: inline-block; + vertical-align: top; + width: 100%; +} + +.models-container { + margin: 0 10px; + height: calc(100vh - 104px); + overflow-y: auto; + transition: all 0.3s ease; +} + +.model-headings { + margin: 10px 10px 0 10px; + background-color: #AEBDCD; + padding: 5px 20px; + border: 1px solid #D9E0E8; + font-weight: bold; + border-radius: 7px 7px 0 0; + border-bottom: 0px; + overflow: hidden; +} + +.model-heading { + padding: 4px 7px; + display: inline-block; + box-sizing: border-box; + width: 190px; + position: relative; + opacity: 1; +} + +.models-container .model { + margin: 0 0 10px 0; + background-color: white; + padding: 10px 20px 25px 20px; + border: 1px solid #D9E0E8; + display: inline-block; + min-width: 100%; + box-sizing: border-box; + position: relative; +} + +.models-container .model-section { + width: 190px; + display: inline-block; + vertical-align: top; +} + +.models-container .model-section .popup-label { + cursor: pointer; +} + +.models-container .kv { + padding: 4px 7px; + cursor: move; + transition: background-color 0.3s ease; + border-radius: 2px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.models-container .nkv { + padding: 4px 7px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + cursor: inherit; +} + +.kv:hover { + background-color: #AEBDCD; +} + +.nkv:hover { + background-color: white; +} + +.triangle-container { + position: absolute; + right: 20px; + top: 0; +} + +.triangle-up, +.triangle-down { + position: absolute; + right: 0; + top: 0; + opacity: 0; + padding: 5px; + transition: all 0.3s ease; + cursor: pointer; +} + +.triangle-container.sorted.asc .triangle-down, +.triangle-container.sorted.dsc .triangle-up { + opacity: 1; +} + +.triangle-container:not(.sorted) > .triangle-down { + opacity: 0.15; +} + +.triangle-container:not(.sorted):hover > .triangle-down { + opacity: 0.4; +} + +.dropdown-sort { + color: rgba(0, 0, 0, 0); + text-shadow: none; + cursor: pointer; + opacity: 0.15; + background-size: 19px 22px; + background-position: right 0px top 3px; + position: absolute; + right: 0; + top: 0; + width: 20px; +} + +.dropdown-sort:not(.sorted):hover { + opacity: 0.4; +} + +.dropdown-sort.sorted { + opacity: 1; +} + +.dropdown-sort.dsc { + background:url("data:image/svg+xml;utf8,"); + background-size: 19px 22px; + background-position: right 0px top 3px; +} + +.model-see-more { + position: absolute; + right: 10px; + bottom: 5px; + color: #00B7FF; + font-size: 12px; + cursor: pointer; + padding-top: 10px; +} + +.model-additional-info { + display: none; + border-top: 1px solid #eee; + margin-top: 15px; + opacity: 0; +} + +.model-additional-info h3 { + font-size: 18px; + margin: 10px 0; + font-weight: 300; +} + +.model-additional-info .kv { + display: inline-block; + vertical-align: top; + width: 176px; +} + +.nkv.json-md-trigger { + color: #00B7FF; + cursor: pointer; +} + +#md-json { + padding: 20px; + max-height: 400px; + overflow-y: scroll; +} + +#md-json .kv { + display: table; + cursor: move; + padding: 4px 7px; +} + +#md-json .nkv { + cursor: initial; +} + +#md-json .nkv:hover { + background-color: #D9E0E8; +} + +li.json-kv.ui-draggable-dragging { + z-index: 2001; + width: auto; + list-style-type: none; +} + +#md-json li.kv.editable-content { + cursor: text; +} + +#md-json li.kv.elt { + cursor: text; +} + +.save-button, .edit-button { + float:right; +} + +.save-button:disabled { + cursor: not-allowed; +} + +/* + * Groups + */ + +.groups-bar-container { + display: inline-block; + position: relative; +} + +.groups-bar { + height: calc(100vh - 69px); + width: 25px; + display: inline-block; + vertical-align: top; + margin: 10px 0px 10px 15px; + display: none; + overflow-y: hidden; +} + +.groups-bar.overflow { + height: calc(100vh - 109px); + margin: 30px 0px 30px 15px; +} + +.data-table.groups-open { + width: calc(100% - 40px); +} + +.model .groups-open, +.groups-open { + width: 182px; +} + +.group-block { + min-height: 10px; + margin-bottom: 2px; + width: 100%; + opacity: 0.2; + cursor: pointer; + transition: all 0.3s ease; +} + +.group-block:not(.group-selected):hover { + opacity: 0.5; +} + +.group-selected { + opacity:0.7; +} + +.group-tooltip { + padding: 10px; + background-color: rgba(64, 64, 64, 0.95); + position: absolute; + left: 46px; + z-index: 1; +} + +.group-tooltip-keys, +.group-tooltip-values { + display: inline-block; + vertical-align: top; +} + +.group-tooltip-key { + color: #aaa; + max-width: 150px; + text-align: right; + padding-right: 1px; + margin-bottom: 1px; +} + +.group-tooltip-value { + color: white; + margin-bottom: 1px; +} + +.overflow-caret { + position: absolute; + opacity: 0.3; + right: 0px; + transition: all 0.2s ease; + cursor: pointer; +} + +.overflow-caret:hover { + opacity: 0.7; +} + +.overflow-caret img{ + width: 25px; +} + +.overflow-up { + top: 5px; +} + +.overflow-down { + bottom: 2px; +} + +/* + * Filters + */ + +.filter-area-highlight, .set-headings-highlight { + background-color: rgba(130,130,130, 0.2); + border: 1px solid #AEBDCD; + border-bottom: none; +} + +.filter-button, .set-headings-button { + width: 100%; + padding: 5px; + background-color: #AEBDCD; + font-size: 14px; + color: white; + font-weight: 300; + cursor: pointer; + transition: all 0.3s ease; +} + +.filter-button:hover, .set-headings-button:hover { + background-color: #a0b2c5; +} + +.filter-button-disabled, .set-headings-button-disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.kv.ui-draggable-dragging { + background-color: #AEBDCD; + z-index: 3; + width: 176px; + padding: 4px 7px; + border-radius: 2px; + pointer-events:none; +} + +.groups-open .kv.ui-draggable-dragging { + width: 166px; +} + +.filter, .range, .sort { + margin: 10px 0; + padding: 5px; + background-color: #AEBDCD; + border-radius: 2px; + color: #2c3e50; + position: relative; +} + +.filter-key, .filter-val, .range-key { + display: inline-block; + vertical-align: top; + margin-left: 5px; + cursor: default; +} + +.filter-key { + white-space: nowrap; + max-width: 80px; + overflow: hidden; + text-overflow: ellipsis; +} + +.filter-val { + color: white; +} + +.selected-heading { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; + margin-left: 5px; + cursor: default; +} + +.item-close, +.filter-close, +.range-close, +.sort-close { + color: #D9E0E8; + cursor: pointer; + transition: all 0.3s ease; + position: absolute; + right: 6px; + top: 5px; +} + +.item-close:hover, +.filter-close:hover, +.range-close:hover, +.sort-close:hover { + color: #000; +} + +.range-options { + margin: 5px 0; +} + +.range-options select { + width: 45px; + color: black; +} + +.range-input { + text-align: right; + width: calc(100% - 60px); +} + +.filter-expand { + color: #D9E0E8; + cursor: pointer; + transition: all 0.3s ease; + font-size: 11px; + line-height: 18px; + opacity: 0.7; + position: absolute; + top: 4px; + right: 16px; +} + +.filter-expand:hover { + color: #2c3e50; + opacity: 0.5; +} + +.filter-options { + padding: 4px 0; + margin: 6px 0 -2px 0; + display: none; +} + +.filter-options input[type=text] { + width: 129px; + padding: 2px 4px; + font-weight: 100; + margin: 0 2px; +} + +.filter-val { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.filter-val.invert:before { + content: "NOT( "; +} + +.filter-val.invert:after { + content: " )"; +} + +.filter-kv-container { + width: calc(100% - 25px); + white-space: nowrap; + display: flex; +} + +/* + * Experiments + */ + +.tab[data-id="1"] { + position: relative; +} + +.experiments-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: calc(100vh - 49px); + background-color: rgba(236,240,241, 0.9); +} + +.experiments-container .kv { + padding: 4px 7px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + cursor: move; + width: 176px; +} + +/* + * Annotations + */ +.nkv.annotation { + position: relative; + padding-right: 16px; +} + +.annotation-create { + position: absolute; + top: 3px; + right: -1px; + line-height: 16px; + font-size: 15px; + cursor: pointer; + transition: all 0.3s ease; + opacity: 0.2; + font-weight: 600; + user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +.annotation-create:hover { + opacity: 1; +} + +.md-input-container { + padding: 10px; + background-color: rgba(174,189,205,0.5); +} + +.md-input { + display: inline-block; + width: calc(100% - 45px); + padding: 5px; + font-size: 15px; + font-weight: 300; + vertical-align: top; + border-radius: 5px; +} + +.md-send { + width: 20px; + display: inline-block; + padding: 5px; + vertical-align: top; + margin-left:2px; + margin-top: -1px; + border-radius: 5px; + opacity: 0.2; + cursor: pointer; + transition: all 0.3s ease; +} + +.md-send.enabled { + opacity: 0.5; +} + +.md-send.enabled:hover { + opacity: 0.8; +} + +.md-model-id { + display: inline-block; +} + +.md-header { + background-color: #2c3e50 + margin: 0; + padding: 10px; + text-align: center; + font-size: 18px; + font-weight: 300; + opacity: 0.8; + background: #2c3e50; + border-radius: 3px 3px 0 0; + color: white; +} + +.md-body { + +} + +.md-annotations { + height: 300px; + overflow-y: scroll; + padding: 5px 10px; +} + +.md-annotation { + background-color: white; + padding: 7px; + margin: 5px 2px; + border-radius: 5px; + display: inline-block; + font-size: 15px; +} + +/* Custom table styling */ +#modal-table { + width: auto; +} + +#md-table { + padding: 15px; +} + +#md-table table { + border-collapse: collapse; + width: 100%; + overflow: auto; + display: block; +} + +#md-table .model-headings { + background-color: #AEBDCD; +} + +#md-table th, #md-table td { + text-align: center; + padding: 8px; +} + +#md-table tr { + background-color: white; + border: 1px solid #D9E0E8; +} + +svg { + font: 10px sans-serif; +} + +.background path { + fill: none; + stroke: #ddd; + shape-rendering: crispEdges; +} + +.foreground path { + fill: none; + stroke: steelblue; +} + +.brush .extent { + fill-opacity: .3; + stroke: #fff; + shape-rendering: crispEdges; +} + +.axis line, +.axis path { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} + +.axis text { + text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; + cursor: move; +} diff --git a/manager/modeldb/frontend/public/css/pipeline.css b/manager/modeldb/frontend/public/css/pipeline.css new file mode 100644 index 00000000000..b9c546775ec --- /dev/null +++ b/manager/modeldb/frontend/public/css/pipeline.css @@ -0,0 +1,9 @@ +#network { + background-color: white; + margin: 10px; +} + +.models-container { + height: auto; + overflow: initial; +} \ No newline at end of file diff --git a/manager/modeldb/frontend/public/css/projects.css b/manager/modeldb/frontend/public/css/projects.css new file mode 100644 index 00000000000..bf395b844d9 --- /dev/null +++ b/manager/modeldb/frontend/public/css/projects.css @@ -0,0 +1,174 @@ +a { + color: black; +} + +input { + padding: 5px; + font-size: 15px; + font-weight: 100; + vertical-align: top; + height: 17px; + margin: 1px 0 -1px 10px; + width: 300px; +} + +input:focus { + outline: none; +} + +.projects { + margin: 10px; +} + +.project h2 { + font-size: 16px; + font-weight: 300; +} + +.project { + border: 1px solid #D9E0E8; + background-color: white; + padding: 10px; + cursor: pointer; + transition: all 0.3s ease; + margin-bottom: 10px; + position: relative; + border-radius: 5px; +} + +.project:hover { + box-shadow: 1px 1px 10px #AEBDCD; + background-color: #f4f4f4; +} + +.project-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 4px; + line-height: 22px; +} + +.project-num-models { + position: absolute; + top: 10px; + right: 10px; + line-height: 22px; +} + +.project-info-container { + display: inline-block; + vertical-align: top; + width: 48%; + margin-right: 1%; +} + +.project-date { + color: #aaa; +} + +.project-desc { + margin-bottom: 20px; +} + +.project-model { + display: inline-block; +} + +.project-model::after { + content:','; +} + +.project-model:last-child::after { + content: ''; +} + +.project-metrics-container { + margin-top: -26px; +} + +.project-metrics-column { + display: inline-block; + vertical-align: top; + width: 100px; + margin-right: -3px; +} + +.project-min-models, +.project-max-models, +.project-avg-models { + text-align: right; +} + +.project-metrics-column h2 { + padding: 4px 7px; + box-sizing: border-box; + margin: 2px 0; + font-weight: 400; + border-bottom: 1px solid #AEBDCD;; +} + +.project-metric-name { + padding: 2px 7px; +} + +.project-min-model, +.project-max-model, +.project-avg-model { + cursor: pointer; + padding: 2px 7px; +} + +.project-model-types-bar { + white-space: nowrap; + overflow: hidden; +} + +.project-model-type { + height: 25px; + opacity: 0.2; + cursor: pointer; + transition: all 0.3s ease; + margin-right: 2px; + display: inline-block; +} + +.project-model-type:hover { + opacity: 0.5; +} + +@media (max-width: 1000px) { + .project-info-container { + width: 100%; + display: block; + } + + .project-metrics-container { + margin-top: 20px; + } +} + +.projects-loader { + border: 1px solid #D9E0E8; + padding: 10px; + margin: 10px; + position: relative; + border-radius: 5px; + height: 25px; + color: white; + font-size: 20px; + background-color: rgb(103, 110, 129); + background-color: rgb(55, 57, 64); + display: none; + font-weight: 100; + line-height: 25px; + opacity: 0.9; + overflow: hidden; +} + +.projects-loader img { + float: left; + width: 60px; + margin-top: -5px; + margin-right: -5px; + margin-left: -15px; +} \ No newline at end of file diff --git a/manager/modeldb/frontend/public/css/style.css b/manager/modeldb/frontend/public/css/style.css new file mode 100644 index 00000000000..19b76c8c9d7 --- /dev/null +++ b/manager/modeldb/frontend/public/css/style.css @@ -0,0 +1,193 @@ +body { + margin: 0px; + font-family: 'Lato', sans-serif; + font-weight: 300; + background-color: #ecf0f1; + font-size: 13px; +} + +a { + text-decoration: none; + color: #00B7FF; +} + +button { + border: none; + background-color: #AEBDCD; + font-size: 14px; + color: white; + font-weight: 300; + cursor: pointer; + transition: all 0.3s ease; + padding: 5px; +} + +button:hover { + background-color: #a0b2c5; +} + +button:focus { + outline: none; +} + +h2 { + margin: 0 0 5px 0; +} + +nav { + background-color: #2c3e50; + color: white; + padding: 10px; + font-size: 24px; + font-weight: 300; + line-height: 29px; + box-sizing: border-box; + width: 100%; + opacity: 0.95; + z-index: 2; + position: fixed; + height: 49px; +} + +nav .logo { + display: inline-block; +} + +nav .logo a { + color: white; +} + +nav .path { + display: inline-block; + color: #95a5a6; +} + +nav .nav-link { + color: #95a5a6; + float: right; + font-size: 20px; + line-height: 29px; + font-weight: 100; + transition: all 0.3s ease; + margin: 0 7px; + cursor: pointer; +} + +nav .nav-link:hover { + color: white; +} + +.container { + padding-top: 49px; +} + + +select { + background:url("data:image/svg+xml;utf8,"); + background-color:#AEBDCD; + background-repeat:no-repeat; + background-position: right 10px top 7px; + background-size: 16px 16px; + color:white; + padding:5px; + font-size:14px; + font-weight: 300; + color:#fff; + text-align:center; + text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25); + border-radius:3px; + -webkit-border-radius:3px; + -webkit-appearance: none; + border:0; + outline:0; + -webkit-transition:0.3s ease all; + -moz-transition:0.3s ease all; + -ms-transition:0.3s ease all; + -o-transition:0.3s ease all; + transition:0.3s ease all; +} + +select:focus, select:active { + border:0; + outline:0; +} + +input { + border: none; + border-bottom: solid 2px #c9c9c9; + transition: border 0.3s; + outline: none; + border-radius: 5px; +} + +input:focus { + border-bottom: solid 2px rgba(44,62,80,0.5); + outline: none; +} + +.loader { + display: none; + background-color: rgb(103, 110, 129); + width: 100%; + height: calc(100vh - 49px); + line-height: calc(100vh - 49px); + position: fixed; + left: 0; + top: 49px; + text-align: center; + opacity: 0.95; + z-index: 5; +} + +.loader img { + vertical-align: middle; + height: 450px; + width: 650px; +} + +.loader-text { + color: white; + font-size: 25px; + position: absolute; + font-weight: 100; + top: 40px; + width: 100%; + line-height: 30px; +} + +.tooltip { + padding: 10px; + background-color: rgba(64, 64, 64, 0.95); + position: absolute; + z-index: 2; +} + +.tooltip:after { + content: ''; + position: absolute; + left:50%; + bottom: -10px; + width: 0; + height: 0; + border-right: 10px solid transparent; + border-left: 0px solid transparent; + border-top: 10px solid rgba(64, 64, 64, 0.95); +} + +.tooltip-keys, +.tooltip-values { + display: inline-block; + vertical-align: top; +} + +.tooltip-key { + color: #aaa; + text-align: right; + padding-right: 1px; + margin-bottom: 1px; +} + +.tooltip-value { + color: white; + margin-bottom: 1px; +} \ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/experiments.ejs b/manager/modeldb/frontend/public/ejs/experiments.ejs new file mode 100644 index 00000000000..fb551ae3ed7 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/experiments.ejs @@ -0,0 +1,23 @@ +
+
+ +
+ <% for (var i=0; i +
+ Experiment ID: <%= experiments[i].id %> +
+
+ <%= experiments[i].name %> +
+
+ <%= experiments[i].description %> +
+ <% } %> +
+ +
+
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/filter.ejs b/manager/modeldb/frontend/public/ejs/filter.ejs new file mode 100644 index 00000000000..940fc00d5eb --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/filter.ejs @@ -0,0 +1,26 @@ +
+
+
+ <%= key %> +
: +
+ <%= val %> +
+
+
X
+
+ +
+ + Invert +
+ +
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/group-tooltip.ejs b/manager/modeldb/frontend/public/ejs/group-tooltip.ejs new file mode 100644 index 00000000000..ffcb5edbdd9 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/group-tooltip.ejs @@ -0,0 +1,17 @@ +
+
+
<%= key %>:
+ <% if (description) { %> +
description:
+ <% } %> +
models:
+
+ +
+
<%= value %>
+ <% if (description) { %> +
<%= description %>
+ <% } %> +
<%= num %>
+
+
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/model.ejs b/manager/modeldb/frontend/public/ejs/model.ejs new file mode 100644 index 00000000000..e3784e99e8c --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/model.ejs @@ -0,0 +1,222 @@ +
+
+ +
+ + "> +
" + > + Model ID: <%= model["id"] %> +
+
+ + <% if (model["md.NAME"]) { %> +
" + > + Model Name: <%= model["md.NAME"] %> +
+ <% } %> + +
" + > + Experiment Run ID: <%= model["Experiment Run ID"] %> +
+ +
" + > + Experiment ID: <%= model["Experiment ID"] %> +
+ +
+ + +
+ +
" + > + DataFrame ID: <%= model["DataFrame ID"] %> +
+ + <% if (model["DF Tag"]) { %> +
" + > + DF Tag: <%= model["DF Tag"] %> +
+ <% } %> + + <% if (model["DF Filepath"]) { %> +
" + > + DF Filepath: <%= model["DF Filepath"] %> +
+ <% } %> + + <% if (model["df_metadata"].length > 0) { %> + + <% } %> +
+ + +
+ +
" + > + Type: <%= model["Type"] %> +
+ + <% if (model["Spec Tag"]) { %> +
" + > + Spec Tag: <%= model["Spec Tag"] %> +
+ <% } %> + + +
+ + +
+ <% for (var j=0; j +
+ <%= model.metrics[j].key %>: <%= model.metrics[j].val %> +
+ <% } %> +
+ + +
+
+ Notes: + + <% if (model.annotations.length > 0) { %> + <%= model.annotations[model.annotations.length - 1].replace(/Transformer\([0-9]*\)\s/, "") %> + <% } %> + +
" + > + [+] +
+
+
+
+ + <% if (model["Code SHA"]) { %> +
" + > + Code SHA: <%= model["Code SHA"] %> +
+ <% } %> + + <% if (model["Filepath"]) { %> +
" + > + Model Filepath: <%= model["Filepath"] %> +
+ <% } %> + + <% if (model["timestamp"]) { %> + <% ISO_date = model["timestamp"].toISOString().replace(/T/, ' ').replace(/\..+/, '') %> +
+ Timestamp: <%= ISO_date %> +
+ <% } %> + + + <% if (model["metadata"]) { %> +
"" + > + Metadata +
+ <% } %> +
+ +
+ See More +
+ +
+

Additional Info

+ +
" + > + DataFrame ID: <%= model["DataFrame ID"] %> +
+ +
" + > + Experiment Run ID: <%= model["Experiment Run ID"] %> +
+ +
" + > + Experiment ID: <%= model["Experiment ID"] %> +
+
+
+
diff --git a/manager/modeldb/frontend/public/ejs/range.ejs b/manager/modeldb/frontend/public/ejs/range.ejs new file mode 100644 index 00000000000..b4553bd7049 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/range.ejs @@ -0,0 +1,11 @@ +
+
<%= key %>
+
X
+
+ + +
+
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/selected-item.ejs b/manager/modeldb/frontend/public/ejs/selected-item.ejs new file mode 100644 index 00000000000..c0dab2202b8 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/selected-item.ejs @@ -0,0 +1,14 @@ +
+
+
+ <%= key %> +
+
+
X
+ +
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/table-cell.ejs b/manager/modeldb/frontend/public/ejs/table-cell.ejs new file mode 100644 index 00000000000..a934057513f --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/table-cell.ejs @@ -0,0 +1,3 @@ + + <%= value %> + \ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/table-heading.ejs b/manager/modeldb/frontend/public/ejs/table-heading.ejs new file mode 100644 index 00000000000..4ecf9ab46f4 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/table-heading.ejs @@ -0,0 +1,3 @@ + + <%= heading %> + \ No newline at end of file diff --git a/manager/modeldb/frontend/public/ejs/tooltip.ejs b/manager/modeldb/frontend/public/ejs/tooltip.ejs new file mode 100644 index 00000000000..1bfdc109bf9 --- /dev/null +++ b/manager/modeldb/frontend/public/ejs/tooltip.ejs @@ -0,0 +1,14 @@ +
+
+ <% for (var i=0; i +
<%= keys[i] %>:
+ <% } %> +
+ +
+ <% for (var i=0; i +
<%= values[i] %>
+ <% } %> +
+ +
\ No newline at end of file diff --git a/manager/modeldb/frontend/public/images/caret-down.png b/manager/modeldb/frontend/public/images/caret-down.png new file mode 100644 index 00000000000..6ec112c153c Binary files /dev/null and b/manager/modeldb/frontend/public/images/caret-down.png differ diff --git a/manager/modeldb/frontend/public/images/caret-up.png b/manager/modeldb/frontend/public/images/caret-up.png new file mode 100644 index 00000000000..33574c5d4e6 Binary files /dev/null and b/manager/modeldb/frontend/public/images/caret-up.png differ diff --git a/manager/modeldb/frontend/public/images/charts.png b/manager/modeldb/frontend/public/images/charts.png new file mode 100644 index 00000000000..d28b784d25a Binary files /dev/null and b/manager/modeldb/frontend/public/images/charts.png differ diff --git a/manager/modeldb/frontend/public/images/data.png b/manager/modeldb/frontend/public/images/data.png new file mode 100644 index 00000000000..db805de9eae Binary files /dev/null and b/manager/modeldb/frontend/public/images/data.png differ diff --git a/manager/modeldb/frontend/public/images/loading.gif b/manager/modeldb/frontend/public/images/loading.gif new file mode 100644 index 00000000000..da1285470e0 Binary files /dev/null and b/manager/modeldb/frontend/public/images/loading.gif differ diff --git a/manager/modeldb/frontend/public/images/loading2.gif b/manager/modeldb/frontend/public/images/loading2.gif new file mode 100644 index 00000000000..167f3d1366c Binary files /dev/null and b/manager/modeldb/frontend/public/images/loading2.gif differ diff --git a/manager/modeldb/frontend/public/images/loading3.gif b/manager/modeldb/frontend/public/images/loading3.gif new file mode 100644 index 00000000000..1fe22daa21f Binary files /dev/null and b/manager/modeldb/frontend/public/images/loading3.gif differ diff --git a/manager/modeldb/frontend/public/images/send.png b/manager/modeldb/frontend/public/images/send.png new file mode 100644 index 00000000000..5e789926017 Binary files /dev/null and b/manager/modeldb/frontend/public/images/send.png differ diff --git a/manager/modeldb/frontend/public/js/annotations.js b/manager/modeldb/frontend/public/js/annotations.js new file mode 100644 index 00000000000..17b054324dd --- /dev/null +++ b/manager/modeldb/frontend/public/js/annotations.js @@ -0,0 +1,82 @@ +$(function() { + $(document).on('click', '.md-trigger', function(event) { + var modelId = $(this).data('id'); + var experimentRunId = $(this).data('experimentrunid'); + $('.md-model-id').html(modelId); + $('.md-input').data('id', modelId); + $('.md-input').data('experimentRunId', experimentRunId); + + // fetch annotations + var root_path = $('.body').context.body.dataset.root; + $.ajax({ + url: root_path + modelId + '/annotations', + type: 'GET', + success: function(response) { + $('.md-annotations').html(""); + for (var i=0; i
' + + response[i].replace(/Transformer\([0-9]*\)\s/, "") + + '
')); + } + $('.md-annotations')[0].scrollTop = $('.md-annotations')[0].scrollHeight; + $('#modal-1').addClass('md-show'); + setTimeout(function(){ + // wasn't working without the timeout + $('.md-input').focus(); + }, 100); + } + }); + }); + + $(document).on('click', '.md-close, .md-overlay', function(event) { + $('.md-modal').removeClass('md-show'); + }); + + $(document).on('keyup', '.md-input', function(event) { + var val = $(event.target).val(); + var code = event.which; + var modelId = $(event.target).data('id'); + var experimentRunId = $(event.target).data('experimentRunId'); + + if (code == 13) { + storeAnnotation(modelId, experimentRunId, val); + $(event.target).val(""); + } else { + if (val.length > 0) { + $('.md-send').addClass('enabled'); + } else { + $('.md-send').removeClass('enabled'); + } + } + }); + + $(document).on('click', '.md-send.enabled', function(event) { + var input = $('.md-input'); + var modelId = input.data('id'); + var experimentRunId = input.data('experimentRunId'); + var val = input.val(); + storeAnnotation(modelId, experimentRunId, val); + input.val(""); + }); + + function storeAnnotation(modelId, experimentRunId, string) { + var data = []; + data.push({name:"string", value:string}); + data.push({name:"experimentRunId", value:experimentRunId}); + var root_path = $('.body').context.body.dataset.root; + $.ajax({ + url: root_path + '/models/' + modelId + '/annotations', + type: "POST", + data: data, + dataType: "json", + success: function(response) { + // add annotation to modal + $('.md-annotations').append($('
' + string + '
')); + $('.md-annotations').animate({scrollTop: $('.md-annotations')[0].scrollHeight + 'px'}); + + // update annotation message in data table + $('.annotation-message[data-id="' + modelId + '"]').html(string); + } + }); + }; +}); diff --git a/manager/modeldb/frontend/public/js/charts.js b/manager/modeldb/frontend/public/js/charts.js new file mode 100644 index 00000000000..2782c1ea097 --- /dev/null +++ b/manager/modeldb/frontend/public/js/charts.js @@ -0,0 +1,176 @@ +(function ($) { + $.fn.findByData = function (prop, val) { + var $self = this; + if (typeof val === 'undefined') { + return $self.filter( + function () { return typeof $(this).data(prop) !== 'undefined'; } + ); + } + return $self.filter( + function () { return $(this).data(prop) == val; } + ); + }; +})(window.jQuery); + +$(function() { + + var xaxis = {}; + var yaxis = {}; + var groupby = {}; + var grouptable = {}; + var vlSpec; + var embedSpec; + + // don't create these groups + var groupFilter = { + "metadata": true, + "hyperparameters": true, + "Model ID": true, + "Project ID": true + } + + $(document).on('click', '.compare-button', function(event) { + updateVega(); + }); + + $(document).on('click', '.chart-toggle', function(event) { + var show = $(event.target).data('show'); + if (!show) { + $(event.target).data('show', true); + $(event.target).html("▼"); + $('.models-container').addClass('models-container-hide'); + $('.chart-container').addClass('chart-container-show'); + } else { + $(event.target).data('show', false); + $(event.target).html("▲"); + $('.models-container').removeClass('models-container-hide'); + $('.chart-container').removeClass('chart-container-show'); + } + }); + + function init() { + var kvs = $('.kv'); + for (var i=0; i").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('