Repository: dragonflyoss/Dragonfly2
Pull request #1530 (2022-08-04 14:23:16)

feat: manager adds model and model version api
<!--- Provide a general summary of your changes in the Title above -->

## Description
- Manager adds model and model version api.
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#1530](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (8005942) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/b0d06eb8a39666f8c1d914a47ed16f86830783ca?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (b0d06eb) will **increase** coverage by `0.07%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main    #1530      +/-   ##
==========================================
+ Coverage   43.17%   43.24%   +0.07%     
==========================================
  Files         116      116              
  Lines       10824    10824              
==========================================
+ Hits         4673     4681       +8     
+ Misses       5662     5658       -4     
+ Partials      489      485       -4     
```

| Flag | Coverage Δ | |
|---|---|---|
| Object-compatibility-e2etests | `?` | |
| e2etests | `?` | |
| unittests | `43.24% <ø> (+0.07%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [...ent/daemon/peer/peertask\_piecetask\_synchronizer.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/1530/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX3BpZWNldGFza19zeW5jaHJvbml6ZXIuZ28=) | `48.91% <0.00%> (+3.46%)` | :arrow_up: |


Patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5c00a161..a2221202 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -17,12 +17,6 @@ jobs:
     steps:
       - name: Checkout code
         uses: actions/checkout@v2
-        with:
-          submodules: recursive
-
-      - name: Build manager console assets
-        run: |-
-          make build-manager-console
 
       - name: Golangci lint
         uses: golangci/golangci-lint-action@v2
diff --git a/api/manager/docs.go b/api/manager/docs.go
index 4af62951..339ff640 100644
--- a/api/manager/docs.go
+++ b/api/manager/docs.go
@@ -76,7 +76,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -453,7 +453,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json bucket",
+                "description": "Create by json bucket",
                 "consumes": [
                     "application/json"
                 ],
@@ -623,7 +623,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -873,7 +873,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1094,7 +1094,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1326,7 +1326,7 @@ const docTemplate = `{
         },
         "/preheats": {
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1678,7 +1678,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1944,7 +1944,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2110,6 +2110,521 @@ const docTemplate = `{
                 }
             }
         },
+        "/schedulers/{id}/models": {
+            "get": {
+                "description": "Get Models",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Models",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.Model"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Create Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Model",
+                        "name": "Model",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateModelRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}": {
+            "get": {
+                "description": "Get Model by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Destroy Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}/versions": {
+            "get": {
+                "description": "Get Model Versions by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model Versions",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.ModelVersion"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Create Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "ModelVersion",
+                        "name": "ModelVersion",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateModelVersionRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}/versions/{version_id}": {
+            "get": {
+                "description": "Get Model Version by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Destroy Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{scheduler_id}/models/{model_id}": {
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Update Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Model",
+                        "name": "Model",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateModelRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{scheduler_id}/models/{model_id}/versions/{version_id}": {
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Update Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "ModelVersion",
+                        "name": "ModelVersion",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateModelVersionRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
         "/security-groups": {
             "get": {
                 "description": "Get SecurityGroups",
@@ -2165,7 +2680,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2528,7 +3043,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2789,7 +3304,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -3100,7 +3615,7 @@ const docTemplate = `{
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -3692,15 +4207,9 @@ const docTemplate = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "download_rate_limit": {
                     "type": "integer"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3719,9 +4228,6 @@ const docTemplate = `{
                 "state": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "url": {
                     "type": "string"
                 },
@@ -3733,22 +4239,58 @@ const docTemplate = `{
                 }
             }
         },
-        "model.Config": {
+        "model.Assertion": {
             "type": "object",
             "properties": {
-                "bio": {
-                    "type": "string"
+                "fieldIndexMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
                 },
-                "created_at": {
+                "key": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
+                "policy": {
+                    "type": "array",
+                    "items": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    }
                 },
-                "name": {
+                "policyMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "rm": {},
+                "tokens": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "value": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.AssertionMap": {
+            "type": "object",
+            "additionalProperties": {
+                "$ref": "#/definitions/model.Assertion"
+            }
+        },
+        "model.Config": {
+            "type": "object",
+            "properties": {
+                "bio": {
                     "type": "string"
                 },
-                "updated_at": {
+                "name": {
                     "type": "string"
                 },
                 "user_id": {
@@ -3774,12 +4316,6 @@ const docTemplate = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "result": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
@@ -3804,9 +4340,6 @@ const docTemplate = `{
                 "type": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "user_id": {
                     "type": "integer"
                 }
@@ -3824,35 +4357,20 @@ const docTemplate = `{
                 "client_secret": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
                 "redirect_url": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
         "model.Scheduler": {
             "type": "object",
             "properties": {
-                "created_at": {
-                    "type": "string"
-                },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
@@ -3873,9 +4391,6 @@ const docTemplate = `{
                 },
                 "state": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3894,12 +4409,6 @@ const docTemplate = `{
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -3923,9 +4432,6 @@ const docTemplate = `{
                     "items": {
                         "$ref": "#/definitions/model.SeedPeerCluster"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3935,12 +4441,6 @@ const docTemplate = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3949,9 +4449,6 @@ const docTemplate = `{
                     "items": {
                         "$ref": "#/definitions/model.SecurityRule"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3961,15 +4458,9 @@ const docTemplate = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "domain": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3981,27 +4472,18 @@ const docTemplate = `{
                     "items": {
                         "$ref": "#/definitions/model.SecurityGroup"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
         "model.SeedPeer": {
             "type": "object",
             "properties": {
-                "created_at": {
-                    "type": "string"
-                },
                 "download_port": {
                     "type": "integer"
                 },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
@@ -4028,9 +4510,6 @@ const docTemplate = `{
                 },
                 "type": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4046,12 +4525,6 @@ const docTemplate = `{
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -4075,9 +4548,6 @@ const docTemplate = `{
                 },
                 "security_group_id": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4090,15 +4560,9 @@ const docTemplate = `{
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "email": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -4110,9 +4574,6 @@ const docTemplate = `{
                 },
                 "state": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4272,6 +4733,62 @@ const docTemplate = `{
                 }
             }
         },
+        "types.CreateModelRequest": {
+            "type": "object",
+            "required": [
+                "hostname",
+                "id",
+                "ip",
+                "name",
+                "scheduler_id",
+                "version_id"
+            ],
+            "properties": {
+                "hostname": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string",
+                    "enum": [
+                        "evaluator"
+                    ]
+                },
+                "ip": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "scheduler_id": {
+                    "type": "integer"
+                },
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.CreateModelVersionRequest": {
+            "type": "object",
+            "required": [
+                "data",
+                "precision",
+                "recall"
+            ],
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                }
+            }
+        },
         "types.CreateOauthRequest": {
             "type": "object",
             "required": [
@@ -4564,6 +5081,79 @@ const docTemplate = `{
                 }
             }
         },
+        "types.Model": {
+            "type": "object",
+            "required": [
+                "create_at",
+                "hostname",
+                "id",
+                "ip",
+                "name",
+                "scheduler_id",
+                "updated_at",
+                "version_id"
+            ],
+            "properties": {
+                "create_at": {
+                    "type": "string"
+                },
+                "hostname": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "ip": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "scheduler_id": {
+                    "type": "integer"
+                },
+                "updated_at": {
+                    "type": "string"
+                },
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.ModelVersion": {
+            "type": "object",
+            "required": [
+                "create_at",
+                "data",
+                "id",
+                "precision",
+                "recall",
+                "updated_at"
+            ],
+            "properties": {
+                "create_at": {
+                    "type": "string"
+                },
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "id": {
+                    "type": "string"
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                },
+                "updated_at": {
+                    "type": "string"
+                }
+            }
+        },
         "types.ResetPasswordRequest": {
             "type": "object",
             "required": [
@@ -4744,6 +5334,31 @@ const docTemplate = `{
                 }
             }
         },
+        "types.UpdateModelRequest": {
+            "type": "object",
+            "properties": {
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.UpdateModelVersionRequest": {
+            "type": "object",
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                }
+            }
+        },
         "types.UpdateOauthRequest": {
             "type": "object",
             "properties": {
diff --git a/api/manager/swagger.json b/api/manager/swagger.json
index e86f90ca..860bee57 100644
--- a/api/manager/swagger.json
+++ b/api/manager/swagger.json
@@ -69,7 +69,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -446,7 +446,7 @@
                 }
             },
             "post": {
-                "description": "create by json bucket",
+                "description": "Create by json bucket",
                 "consumes": [
                     "application/json"
                 ],
@@ -616,7 +616,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -866,7 +866,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1087,7 +1087,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1319,7 +1319,7 @@
         },
         "/preheats": {
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1671,7 +1671,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -1937,7 +1937,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2103,6 +2103,521 @@
                 }
             }
         },
+        "/schedulers/{id}/models": {
+            "get": {
+                "description": "Get Models",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Models",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.Model"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Create Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Model",
+                        "name": "Model",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateModelRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}": {
+            "get": {
+                "description": "Get Model by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Destroy Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}/versions": {
+            "get": {
+                "description": "Get Model Versions by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model Versions",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/types.ModelVersion"
+                            }
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "post": {
+                "description": "Create by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Create Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "ModelVersion",
+                        "name": "ModelVersion",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.CreateModelVersionRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{id}/models/{model_id}/versions/{version_id}": {
+            "get": {
+                "description": "Get Model Version by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Get Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            },
+            "delete": {
+                "description": "Destroy by id",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Destroy Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": ""
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{scheduler_id}/models/{model_id}": {
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Update Model",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "Model",
+                        "name": "Model",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateModelRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.Model"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
+        "/schedulers/{scheduler_id}/models/{model_id}/versions/{version_id}": {
+            "patch": {
+                "description": "Update by json config",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Model"
+                ],
+                "summary": "Update Model Version",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "id",
+                        "name": "id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "model_id",
+                        "name": "model_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "version_id",
+                        "name": "version_id",
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "description": "ModelVersion",
+                        "name": "ModelVersion",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/types.UpdateModelVersionRequest"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/types.ModelVersion"
+                        }
+                    },
+                    "400": {
+                        "description": ""
+                    },
+                    "404": {
+                        "description": ""
+                    },
+                    "500": {
+                        "description": ""
+                    }
+                }
+            }
+        },
         "/security-groups": {
             "get": {
                 "description": "Get SecurityGroups",
@@ -2158,7 +2673,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2521,7 +3036,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -2782,7 +3297,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -3093,7 +3608,7 @@
                 }
             },
             "post": {
-                "description": "create by json config",
+                "description": "Create by json config",
                 "consumes": [
                     "application/json"
                 ],
@@ -3685,15 +4200,9 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "download_rate_limit": {
                     "type": "integer"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3712,9 +4221,6 @@
                 "state": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "url": {
                     "type": "string"
                 },
@@ -3726,22 +4232,58 @@
                 }
             }
         },
-        "model.Config": {
+        "model.Assertion": {
             "type": "object",
             "properties": {
-                "bio": {
-                    "type": "string"
+                "fieldIndexMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
                 },
-                "created_at": {
+                "key": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
+                "policy": {
+                    "type": "array",
+                    "items": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    }
                 },
-                "name": {
+                "policyMap": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "integer"
+                    }
+                },
+                "rm": {},
+                "tokens": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "value": {
+                    "type": "string"
+                }
+            }
+        },
+        "model.AssertionMap": {
+            "type": "object",
+            "additionalProperties": {
+                "$ref": "#/definitions/model.Assertion"
+            }
+        },
+        "model.Config": {
+            "type": "object",
+            "properties": {
+                "bio": {
                     "type": "string"
                 },
-                "updated_at": {
+                "name": {
                     "type": "string"
                 },
                 "user_id": {
@@ -3767,12 +4309,6 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "result": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
@@ -3797,9 +4333,6 @@
                 "type": {
                     "type": "string"
                 },
-                "updated_at": {
-                    "type": "string"
-                },
                 "user_id": {
                     "type": "integer"
                 }
@@ -3817,35 +4350,20 @@
                 "client_secret": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
                 "redirect_url": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
         "model.Scheduler": {
             "type": "object",
             "properties": {
-                "created_at": {
-                    "type": "string"
-                },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
@@ -3866,9 +4384,6 @@
                 },
                 "state": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3887,12 +4402,6 @@
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -3916,9 +4425,6 @@
                     "items": {
                         "$ref": "#/definitions/model.SeedPeerCluster"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3928,12 +4434,6 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3942,9 +4442,6 @@
                     "items": {
                         "$ref": "#/definitions/model.SecurityRule"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -3954,15 +4451,9 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "domain": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "name": {
                     "type": "string"
                 },
@@ -3974,27 +4465,18 @@
                     "items": {
                         "$ref": "#/definitions/model.SecurityGroup"
                     }
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
         "model.SeedPeer": {
             "type": "object",
             "properties": {
-                "created_at": {
-                    "type": "string"
-                },
                 "download_port": {
                     "type": "integer"
                 },
                 "host_name": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "idc": {
                     "type": "string"
                 },
@@ -4021,9 +4503,6 @@
                 },
                 "type": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4039,12 +4518,6 @@
                 "config": {
                     "$ref": "#/definitions/model.JSONMap"
                 },
-                "created_at": {
-                    "type": "string"
-                },
-                "id": {
-                    "type": "integer"
-                },
                 "is_default": {
                     "type": "boolean"
                 },
@@ -4068,9 +4541,6 @@
                 },
                 "security_group_id": {
                     "type": "integer"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4083,15 +4553,9 @@
                 "bio": {
                     "type": "string"
                 },
-                "created_at": {
-                    "type": "string"
-                },
                 "email": {
                     "type": "string"
                 },
-                "id": {
-                    "type": "integer"
-                },
                 "location": {
                     "type": "string"
                 },
@@ -4103,9 +4567,6 @@
                 },
                 "state": {
                     "type": "string"
-                },
-                "updated_at": {
-                    "type": "string"
                 }
             }
         },
@@ -4265,6 +4726,62 @@
                 }
             }
         },
+        "types.CreateModelRequest": {
+            "type": "object",
+            "required": [
+                "hostname",
+                "id",
+                "ip",
+                "name",
+                "scheduler_id",
+                "version_id"
+            ],
+            "properties": {
+                "hostname": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string",
+                    "enum": [
+                        "evaluator"
+                    ]
+                },
+                "ip": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "scheduler_id": {
+                    "type": "integer"
+                },
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.CreateModelVersionRequest": {
+            "type": "object",
+            "required": [
+                "data",
+                "precision",
+                "recall"
+            ],
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                }
+            }
+        },
         "types.CreateOauthRequest": {
             "type": "object",
             "required": [
@@ -4557,6 +5074,79 @@
                 }
             }
         },
+        "types.Model": {
+            "type": "object",
+            "required": [
+                "create_at",
+                "hostname",
+                "id",
+                "ip",
+                "name",
+                "scheduler_id",
+                "updated_at",
+                "version_id"
+            ],
+            "properties": {
+                "create_at": {
+                    "type": "string"
+                },
+                "hostname": {
+                    "type": "string"
+                },
+                "id": {
+                    "type": "string"
+                },
+                "ip": {
+                    "type": "string"
+                },
+                "name": {
+                    "type": "string"
+                },
+                "scheduler_id": {
+                    "type": "integer"
+                },
+                "updated_at": {
+                    "type": "string"
+                },
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.ModelVersion": {
+            "type": "object",
+            "required": [
+                "create_at",
+                "data",
+                "id",
+                "precision",
+                "recall",
+                "updated_at"
+            ],
+            "properties": {
+                "create_at": {
+                    "type": "string"
+                },
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "id": {
+                    "type": "string"
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                },
+                "updated_at": {
+                    "type": "string"
+                }
+            }
+        },
         "types.ResetPasswordRequest": {
             "type": "object",
             "required": [
@@ -4737,6 +5327,31 @@
                 }
             }
         },
+        "types.UpdateModelRequest": {
+            "type": "object",
+            "properties": {
+                "version_id": {
+                    "type": "string"
+                }
+            }
+        },
+        "types.UpdateModelVersionRequest": {
+            "type": "object",
+            "properties": {
+                "data": {
+                    "type": "array",
+                    "items": {
+                        "type": "integer"
+                    }
+                },
+                "precision": {
+                    "type": "number"
+                },
+                "recall": {
+                    "type": "number"
+                }
+            }
+        },
         "types.UpdateOauthRequest": {
             "type": "object",
             "properties": {
diff --git a/api/manager/swagger.yaml b/api/manager/swagger.yaml
index 4cd21011..86f34f45 100644
--- a/api/manager/swagger.yaml
+++ b/api/manager/swagger.yaml
@@ -4,12 +4,8 @@ definitions:
     properties:
       bio:
         type: string
-      created_at:
-        type: string
       download_rate_limit:
         type: integer
-      id:
-        type: integer
       name:
         type: string
       scheduler_clusters:
@@ -22,8 +18,6 @@ definitions:
         type: array
       state:
         type: string
-      updated_at:
-        type: string
       url:
         type: string
       user:
@@ -31,18 +25,42 @@ definitions:
       user_id:
         type: integer
     type: object
+  model.Assertion:
+    properties:
+      fieldIndexMap:
+        additionalProperties:
+          type: integer
+        type: object
+      key:
+        type: string
+      policy:
+        items:
+          items:
+            type: string
+          type: array
+        type: array
+      policyMap:
+        additionalProperties:
+          type: integer
+        type: object
+      rm: {}
+      tokens:
+        items:
+          type: string
+        type: array
+      value:
+        type: string
+    type: object
+  model.AssertionMap:
+    additionalProperties:
+      $ref: '#/definitions/model.Assertion'
+    type: object
   model.Config:
     properties:
       bio:
         type: string
-      created_at:
-        type: string
-      id:
-        type: integer
       name:
         type: string
-      updated_at:
-        type: string
       user_id:
         type: integer
       value:
@@ -58,10 +76,6 @@ definitions:
         $ref: '#/definitions/model.JSONMap'
       bio:
         type: string
-      created_at:
-        type: string
-      id:
-        type: integer
       result:
         $ref: '#/definitions/model.JSONMap'
       scheduler_clusters:
@@ -78,8 +92,6 @@ definitions:
         type: string
       type:
         type: string
-      updated_at:
-        type: string
       user_id:
         type: integer
     type: object
@@ -91,25 +103,15 @@ definitions:
         type: string
       client_secret:
         type: string
-      created_at:
-        type: string
-      id:
-        type: integer
       name:
         type: string
       redirect_url:
         type: string
-      updated_at:
-        type: string
     type: object
   model.Scheduler:
     properties:
-      created_at:
-        type: string
       host_name:
         type: string
-      id:
-        type: integer
       idc:
         type: string
       ip:
@@ -124,8 +126,6 @@ definitions:
         type: integer
       state:
         type: string
-      updated_at:
-        type: string
     type: object
   model.SchedulerCluster:
     properties:
@@ -137,10 +137,6 @@ definitions:
         $ref: '#/definitions/model.JSONMap'
       config:
         $ref: '#/definitions/model.JSONMap'
-      created_at:
-        type: string
-      id:
-        type: integer
       is_default:
         type: boolean
       jobs:
@@ -157,36 +153,24 @@ definitions:
         items:
           $ref: '#/definitions/model.SeedPeerCluster'
         type: array
-      updated_at:
-        type: string
     type: object
   model.SecurityGroup:
     properties:
       bio:
         type: string
-      created_at:
-        type: string
-      id:
-        type: integer
       name:
         type: string
       security_rules:
         items:
           $ref: '#/definitions/model.SecurityRule'
         type: array
-      updated_at:
-        type: string
     type: object
   model.SecurityRule:
     properties:
       bio:
         type: string
-      created_at:
-        type: string
       domain:
         type: string
-      id:
-        type: integer
       name:
         type: string
       proxy_domain:
@@ -195,19 +179,13 @@ definitions:
         items:
           $ref: '#/definitions/model.SecurityGroup'
         type: array
-      updated_at:
-        type: string
     type: object
   model.SeedPeer:
     properties:
-      created_at:
-        type: string
       download_port:
         type: integer
       host_name:
         type: string
-      id:
-        type: integer
       idc:
         type: string
       ip:
@@ -226,8 +204,6 @@ definitions:
         type: string
       type:
         type: string
-      updated_at:
-        type: string
     type: object
   model.SeedPeerCluster:
     properties:
@@ -237,10 +213,6 @@ definitions:
         type: string
       config:
         $ref: '#/definitions/model.JSONMap'
-      created_at:
-        type: string
-      id:
-        type: integer
       is_default:
         type: boolean
       jobs:
@@ -257,8 +229,6 @@ definitions:
         $ref: '#/definitions/model.JSONMap'
       security_group_id:
         type: integer
-      updated_at:
-        type: string
     type: object
   model.User:
     properties:
@@ -266,12 +236,8 @@ definitions:
         type: string
       bio:
         type: string
-      created_at:
-        type: string
       email:
         type: string
-      id:
-        type: integer
       location:
         type: string
       name:
@@ -280,8 +246,6 @@ definitions:
         type: string
       state:
         type: string
-      updated_at:
-        type: string
     type: object
   objectstorage.BucketMetadata:
     properties:
@@ -388,6 +352,45 @@ definitions:
     required:
     - type
     type: object
+  types.CreateModelRequest:
+    properties:
+      hostname:
+        type: string
+      id:
+        enum:
+        - evaluator
+        type: string
+      ip:
+        type: string
+      name:
+        type: string
+      scheduler_id:
+        type: integer
+      version_id:
+        type: string
+    required:
+    - hostname
+    - id
+    - ip
+    - name
+    - scheduler_id
+    - version_id
+    type: object
+  types.CreateModelVersionRequest:
+    properties:
+      data:
+        items:
+          type: integer
+        type: array
+      precision:
+        type: number
+      recall:
+        type: number
+    required:
+    - data
+    - precision
+    - recall
+    type: object
   types.CreateOauthRequest:
     properties:
       bio:
@@ -586,6 +589,58 @@ definitions:
       status:
         type: string
     type: object
+  types.Model:
+    properties:
+      create_at:
+        type: string
+      hostname:
+        type: string
+      id:
+        type: string
+      ip:
+        type: string
+      name:
+        type: string
+      scheduler_id:
+        type: integer
+      updated_at:
+        type: string
+      version_id:
+        type: string
+    required:
+    - create_at
+    - hostname
+    - id
+    - ip
+    - name
+    - scheduler_id
+    - updated_at
+    - version_id
+    type: object
+  types.ModelVersion:
+    properties:
+      create_at:
+        type: string
+      data:
+        items:
+          type: integer
+        type: array
+      id:
+        type: string
+      precision:
+        type: number
+      recall:
+        type: number
+      updated_at:
+        type: string
+    required:
+    - create_at
+    - data
+    - id
+    - precision
+    - recall
+    - updated_at
+    type: object
   types.ResetPasswordRequest:
     properties:
       new_password:
@@ -710,6 +765,22 @@ definitions:
       user_id:
         type: integer
     type: object
+  types.UpdateModelRequest:
+    properties:
+      version_id:
+        type: string
+    type: object
+  types.UpdateModelVersionRequest:
+    properties:
+      data:
+        items:
+          type: integer
+        type: array
+      precision:
+        type: number
+      recall:
+        type: number
+    type: object
   types.UpdateOauthRequest:
     properties:
       bio:
@@ -882,7 +953,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Application
         in: body
@@ -1133,7 +1204,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json bucket
+      description: Create by json bucket
       parameters:
       - description: Bucket
         in: body
@@ -1247,7 +1318,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Config
         in: body
@@ -1414,7 +1485,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Job
         in: body
@@ -1562,7 +1633,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Oauth
         in: body
@@ -1715,7 +1786,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Preheat
         in: body
@@ -1949,7 +2020,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: SchedulerCluster
         in: body
@@ -2127,7 +2198,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: Scheduler
         in: body
@@ -2234,6 +2305,351 @@ paths:
       summary: Update Scheduler
       tags:
       - Scheduler
+  /schedulers/{id}/models:
+    get:
+      consumes:
+      - application/json
+      description: Get Models
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              $ref: '#/definitions/types.Model'
+            type: array
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Models
+      tags:
+      - Model
+    post:
+      consumes:
+      - application/json
+      description: Create by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: Model
+        in: body
+        name: Model
+        required: true
+        schema:
+          $ref: '#/definitions/types.CreateModelRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.Model'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Create Model
+      tags:
+      - Model
+  /schedulers/{id}/models/{model_id}:
+    delete:
+      consumes:
+      - application/json
+      description: Destroy by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Destroy Model
+      tags:
+      - Model
+    get:
+      consumes:
+      - application/json
+      description: Get Model by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.Model'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Model
+      tags:
+      - Model
+  /schedulers/{id}/models/{model_id}/versions:
+    get:
+      consumes:
+      - application/json
+      description: Get Model Versions by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            items:
+              $ref: '#/definitions/types.ModelVersion'
+            type: array
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Model Versions
+      tags:
+      - Model
+    post:
+      consumes:
+      - application/json
+      description: Create by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      - description: ModelVersion
+        in: body
+        name: ModelVersion
+        required: true
+        schema:
+          $ref: '#/definitions/types.CreateModelVersionRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.ModelVersion'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Create Model Version
+      tags:
+      - Model
+  /schedulers/{id}/models/{model_id}/versions/{version_id}:
+    delete:
+      consumes:
+      - application/json
+      description: Destroy by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      - description: version_id
+        in: path
+        name: version_id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: ""
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Destroy Model Version
+      tags:
+      - Model
+    get:
+      consumes:
+      - application/json
+      description: Get Model Version by id
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      - description: version_id
+        in: path
+        name: version_id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.ModelVersion'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Get Model Version
+      tags:
+      - Model
+  /schedulers/{scheduler_id}/models/{model_id}:
+    patch:
+      consumes:
+      - application/json
+      description: Update by json config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      - description: Model
+        in: body
+        name: Model
+        required: true
+        schema:
+          $ref: '#/definitions/types.UpdateModelRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.Model'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Update Model
+      tags:
+      - Model
+  /schedulers/{scheduler_id}/models/{model_id}/versions/{version_id}:
+    patch:
+      consumes:
+      - application/json
+      description: Update by json config
+      parameters:
+      - description: id
+        in: path
+        name: id
+        required: true
+        type: string
+      - description: model_id
+        in: path
+        name: model_id
+        required: true
+        type: string
+      - description: version_id
+        in: path
+        name: version_id
+        required: true
+        type: string
+      - description: ModelVersion
+        in: body
+        name: ModelVersion
+        required: true
+        schema:
+          $ref: '#/definitions/types.UpdateModelVersionRequest'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/types.ModelVersion'
+        "400":
+          description: ""
+        "404":
+          description: ""
+        "500":
+          description: ""
+      summary: Update Model Version
+      tags:
+      - Model
   /security-groups:
     get:
       consumes:
@@ -2275,7 +2691,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: SecurityGroup
         in: body
@@ -2518,7 +2934,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: SecurityRule
         in: body
@@ -2692,7 +3108,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: DNCluster
         in: body
@@ -2900,7 +3316,7 @@ paths:
     post:
       consumes:
       - application/json
-      description: create by json config
+      description: Create by json config
       parameters:
       - description: SeedPeer
         in: body
diff --git a/manager/cache/cache.go b/manager/cache/cache.go
index 2dd8f5fa..b04a6b65 100644
--- a/manager/cache/cache.go
+++ b/manager/cache/cache.go
@@ -103,3 +103,13 @@ func MakeSchedulersCacheKeyForPeer(hostname, ip string) string {
 func MakeBucketsCacheKey(name string) string {
 	return MakeCacheKey(BucketsNamespace, name)
 }
+
+// Make cache model key.
+func MakeModelKey(clusterID uint, hostname, ip, id string) string {
+	return MakeCacheKey(SchedulerNamespace, fmt.Sprintf("%d-%s-%s:models:%s", clusterID, hostname, ip, id))
+}
+
+// Make cache model version key.
+func MakeModelVersionKey(clusterID uint, hostname, ip, id, version string) string {
+	return MakeCacheKey(SchedulerNamespace, fmt.Sprintf("%d-%s-%s:models:%s:versions:%s", clusterID, hostname, ip, id, version))
+}
diff --git a/manager/config/config.go b/manager/config/config.go
index 897fb17b..b6096c94 100644
--- a/manager/config/config.go
+++ b/manager/config/config.go
@@ -163,7 +163,7 @@ type RedisConfig struct {
 	Password string `yaml:"password" mapstructure:"password"`
 
 	// Server cache DB name.
-	CacheDB int `yaml:"cacheDB" mapstructure:"cacheDB"`
+	DB int `yaml:"db" mapstructure:"db"`
 
 	// Server broker DB name.
 	BrokerDB int `yaml:"brokerDB" mapstructure:"brokerDB"`
@@ -272,7 +272,7 @@ func New() *Config {
 				Migrate:  true,
 			},
 			Redis: &RedisConfig{
-				CacheDB:   DefaultRedisCacheDB,
+				DB:        DefaultRedisDB,
 				BrokerDB:  DefaultRedisBrokerDB,
 				BackendDB: DefaultRedisBackendDB,
 			},
@@ -408,8 +408,8 @@ func (cfg *Config) Validate() error {
 		return errors.New("redis requires parameter port")
 	}
 
-	if cfg.Database.Redis.CacheDB < 0 {
-		return errors.New("redis requires parameter cacheDB")
+	if cfg.Database.Redis.DB < 0 {
+		return errors.New("redis requires parameter db")
 	}
 
 	if cfg.Database.Redis.BrokerDB < 0 {
diff --git a/manager/config/constants.go b/manager/config/constants.go
index 8f43ee49..42b750eb 100644
--- a/manager/config/constants.go
+++ b/manager/config/constants.go
@@ -41,8 +41,8 @@ const (
 )
 
 const (
-	// DefaultRedisCacheDB is default db for redis cache.
-	DefaultRedisCacheDB = 0
+	// DefaultRedisDB is default db for redis.
+	DefaultRedisDB = 0
 
 	// DefaultRedisBrokerDB is default db for redis broker.
 	DefaultRedisBrokerDB = 1
diff --git a/manager/database/redis.go b/manager/database/redis.go
index d7f84238..f3767990 100644
--- a/manager/database/redis.go
+++ b/manager/database/redis.go
@@ -31,7 +31,7 @@ func NewRedis(cfg *config.RedisConfig) (*redis.Client, error) {
 	client := redis.NewClient(&redis.Options{
 		Addr:     fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
 		Password: cfg.Password,
-		DB:       cfg.CacheDB,
+		DB:       cfg.DB,
 	})
 
 	if err := client.Ping(context.Background()).Err(); err != nil {
diff --git a/manager/handlers/application.go b/manager/handlers/application.go
index 9ac512ae..d4693df5 100644
--- a/manager/handlers/application.go
+++ b/manager/handlers/application.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create Application
-// @Description create by json config
+// @Description Create by json config
 // @Tags Application
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/bucket.go b/manager/handlers/bucket.go
index 39bae62a..8ad51337 100644
--- a/manager/handlers/bucket.go
+++ b/manager/handlers/bucket.go
@@ -30,7 +30,7 @@ import (
 )
 
 // @Summary Create Bucket
-// @Description create by json bucket
+// @Description Create by json bucket
 // @Tags Bucket
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/config.go b/manager/handlers/config.go
index e91e4100..ace14d77 100644
--- a/manager/handlers/config.go
+++ b/manager/handlers/config.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create Config
-// @Description create by json config
+// @Description Create by json config
 // @Tags Config
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/job.go b/manager/handlers/job.go
index 9b47a086..56befa39 100644
--- a/manager/handlers/job.go
+++ b/manager/handlers/job.go
@@ -12,7 +12,7 @@ import (
 )
 
 // @Summary Create Job
-// @Description create by json config
+// @Description Create by json config
 // @Tags Job
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/model.go b/manager/handlers/model.go
new file mode 100644
index 00000000..44aecf96
--- /dev/null
+++ b/manager/handlers/model.go
@@ -0,0 +1,333 @@
+/*
+ *     Copyright 2022 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package handlers
+
+import (
+	"net/http"
+
+	_ "d7y.io/dragonfly/v2/manager/model" // nolint
+	"d7y.io/dragonfly/v2/manager/types"
+
+	"github.com/gin-gonic/gin"
+)
+
+// @Summary Create Model
+// @Description Create by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param Model body types.CreateModelRequest true "Model"
+// @Success 200 {object} types.Model
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models [post]
+func (h *Handlers) CreateModel(ctx *gin.Context) {
+	var params types.CreateModelParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	var json types.CreateModelRequest
+	if err := ctx.ShouldBindJSON(&json); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	model, err := h.service.CreateModel(ctx.Request.Context(), params, json)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, model)
+}
+
+// @Summary Destroy Model
+// @Description Destroy by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Success 200
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id} [delete]
+func (h *Handlers) DestroyModel(ctx *gin.Context) {
+	var params types.ModelParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	if err := h.service.DestroyModel(ctx.Request.Context(), params); err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.Status(http.StatusOK)
+}
+
+// @Summary Update Model
+// @Description Update by json config
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Param Model body types.UpdateModelRequest true "Model"
+// @Success 200 {object} types.Model
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{scheduler_id}/models/{model_id} [patch]
+func (h *Handlers) UpdateModel(ctx *gin.Context) {
+	var params types.ModelParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	var json types.UpdateModelRequest
+	if err := ctx.ShouldBindJSON(&json); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	model, err := h.service.UpdateModel(ctx.Request.Context(), params, json)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, model)
+}
+
+// @Summary Get Model
+// @Description Get Model by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Success 200 {object} types.Model
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id} [get]
+func (h *Handlers) GetModel(ctx *gin.Context) {
+	var params types.ModelParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	model, err := h.service.GetModel(ctx.Request.Context(), params)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, model)
+}
+
+// @Summary Get Models
+// @Description Get Models
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Success 200 {object} []types.Model
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models [get]
+func (h *Handlers) GetModels(ctx *gin.Context) {
+	var params types.GetModelsParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	models, err := h.service.GetModels(ctx.Request.Context(), params)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, models)
+}
+
+// @Summary Create Model Version
+// @Description Create by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Param ModelVersion body types.CreateModelVersionRequest true "ModelVersion"
+// @Success 200 {object} types.ModelVersion
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id}/versions [post]
+func (h *Handlers) CreateModelVersion(ctx *gin.Context) {
+	var params types.CreateModelVersionParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	var json types.CreateModelVersionRequest
+	if err := ctx.ShouldBindJSON(&json); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	modelVersion, err := h.service.CreateModelVersion(ctx.Request.Context(), params, json)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, modelVersion)
+}
+
+// @Summary Destroy Model Version
+// @Description Destroy by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Param version_id path string true "version_id"
+// @Success 200
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id}/versions/{version_id} [delete]
+func (h *Handlers) DestroyModelVersion(ctx *gin.Context) {
+	var params types.ModelVersionParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	if err := h.service.DestroyModelVersion(ctx.Request.Context(), params); err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.Status(http.StatusOK)
+}
+
+// @Summary Update Model Version
+// @Description Update by json config
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Param version_id path string true "version_id"
+// @Param ModelVersion body types.UpdateModelVersionRequest true "ModelVersion"
+// @Success 200 {object} types.ModelVersion
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{scheduler_id}/models/{model_id}/versions/{version_id} [patch]
+func (h *Handlers) UpdateModelVersion(ctx *gin.Context) {
+	var params types.ModelVersionParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	var json types.UpdateModelVersionRequest
+	if err := ctx.ShouldBindJSON(&json); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	modelVersion, err := h.service.UpdateModelVersion(ctx.Request.Context(), params, json)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, modelVersion)
+}
+
+// @Summary Get Model Version
+// @Description Get Model Version by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Param version_id path string true "version_id"
+// @Success 200 {object} types.ModelVersion
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id}/versions/{version_id} [get]
+func (h *Handlers) GetModelVersion(ctx *gin.Context) {
+	var params types.ModelVersionParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	model, err := h.service.GetModelVersion(ctx.Request.Context(), params)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, model)
+}
+
+// @Summary Get Model Versions
+// @Description Get Model Versions by id
+// @Tags Model
+// @Accept json
+// @Produce json
+// @Param id path string true "id"
+// @Param model_id path string true "model_id"
+// @Success 200 {object} []types.ModelVersion
+// @Failure 400
+// @Failure 404
+// @Failure 500
+// @Router /schedulers/{id}/models/{model_id}/versions [get]
+func (h *Handlers) GetModelVersions(ctx *gin.Context) {
+	var params types.GetModelVersionsParams
+	if err := ctx.ShouldBindUri(&params); err != nil {
+		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
+		return
+	}
+
+	modelVersions, err := h.service.GetModelVersions(ctx.Request.Context(), params)
+	if err != nil {
+		ctx.Error(err) // nolint: errcheck
+		return
+	}
+
+	ctx.JSON(http.StatusOK, modelVersions)
+}
diff --git a/manager/handlers/oauth.go b/manager/handlers/oauth.go
index 67eedfc1..2290ba07 100644
--- a/manager/handlers/oauth.go
+++ b/manager/handlers/oauth.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create Oauth
-// @Description create by json config
+// @Description Create by json config
 // @Tags Oauth
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/preheat.go b/manager/handlers/preheat.go
index 2cb9241d..6be4b32f 100644
--- a/manager/handlers/preheat.go
+++ b/manager/handlers/preheat.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create V1 Preheat
-// @Description create by json config
+// @Description Create by json config
 // @Tags Preheat
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/scheduler.go b/manager/handlers/scheduler.go
index a1b675cf..eaf81ee9 100644
--- a/manager/handlers/scheduler.go
+++ b/manager/handlers/scheduler.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create Scheduler
-// @Description create by json config
+// @Description Create by json config
 // @Tags Scheduler
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/scheduler_cluster.go b/manager/handlers/scheduler_cluster.go
index d10673a0..4700ce55 100644
--- a/manager/handlers/scheduler_cluster.go
+++ b/manager/handlers/scheduler_cluster.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create SchedulerCluster
-// @Description create by json config
+// @Description Create by json config
 // @Tags SchedulerCluster
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/security_group.go b/manager/handlers/security_group.go
index b4401c9b..f5facbb7 100644
--- a/manager/handlers/security_group.go
+++ b/manager/handlers/security_group.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create SecurityGroup
-// @Description create by json config
+// @Description Create by json config
 // @Tags SecurityGroup
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/security_rule.go b/manager/handlers/security_rule.go
index 3a8ef2a3..cfefe8c7 100644
--- a/manager/handlers/security_rule.go
+++ b/manager/handlers/security_rule.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create SecurityRule
-// @Description create by json config
+// @Description Create by json config
 // @Tags SecurityRule
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/seed_peer.go b/manager/handlers/seed_peer.go
index d015a963..f90ac2ea 100644
--- a/manager/handlers/seed_peer.go
+++ b/manager/handlers/seed_peer.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create SeedPeer
-// @Description create by json config
+// @Description Create by json config
 // @Tags SeedPeer
 // @Accept json
 // @Produce json
diff --git a/manager/handlers/seed_peer_cluster.go b/manager/handlers/seed_peer_cluster.go
index f5c0c61f..1c0b9ffe 100644
--- a/manager/handlers/seed_peer_cluster.go
+++ b/manager/handlers/seed_peer_cluster.go
@@ -27,7 +27,7 @@ import (
 )
 
 // @Summary Create SeedPeerCluster
-// @Description create by json config
+// @Description Create by json config
 // @Tags SeedPeerCluster
 // @Accept json
 // @Produce json
diff --git a/manager/middlewares/error.go b/manager/middlewares/error.go
index 297fd89f..0ac17e10 100644
--- a/manager/middlewares/error.go
+++ b/manager/middlewares/error.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/VividCortex/mysqlerr"
 	"github.com/gin-gonic/gin"
+	"github.com/go-redis/redis/v8"
 	"github.com/go-sql-driver/mysql"
 	redigo "github.com/gomodule/redigo/redis"
 	"golang.org/x/crypto/bcrypt"
@@ -111,6 +112,14 @@ func Error() gin.HandlerFunc {
 			}
 		}
 
+		if errors.Is(err.Err, redis.Nil) {
+			c.JSON(http.StatusNotFound, ErrorResponse{
+				Message: http.StatusText(http.StatusNotFound),
+			})
+			c.Abort()
+			return
+		}
+
 		// Unknown error
 		c.JSON(http.StatusInternalServerError, ErrorResponse{
 			Message: err.Err.Error(),
diff --git a/manager/router/router.go b/manager/router/router.go
index 9938fc9e..c75c9a39 100644
--- a/manager/router/router.go
+++ b/manager/router/router.go
@@ -152,6 +152,20 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
 	s.GET(":id", h.GetScheduler)
 	s.GET("", h.GetSchedulers)
 
+	// Model
+	s.POST(":id/models", h.CreateModel)
+	s.DELETE(":id/models/:model_id", h.DestroyModel)
+	s.PATCH(":id/models/:model_id", h.UpdateModel)
+	s.GET(":id/models/:model_id", h.GetModel)
+	s.GET(":id/models", h.GetModels)
+
+	// Model Version
+	s.POST(":id/models/:model_id/versions", h.CreateModelVersion)
+	s.DELETE(":id/models/:model_id/versions/:version_id", h.DestroyModelVersion)
+	s.PATCH(":id/models/:model_id/versions/:version_id", h.UpdateModelVersion)
+	s.GET(":id/models/:model_id/versions/:version_id", h.GetModelVersion)
+	s.GET(":id/models/:model_id/versions", h.GetModelVersions)
+
 	// Application
 	cs := apiv1.Group("/applications", jwt.MiddlewareFunc(), rbac)
 	cs.POST("", h.CreateApplication)
diff --git a/manager/service/mocks/service_mock.go b/manager/service/mocks/service_mock.go
index 2723e9ca..d7ce11f5 100644
--- a/manager/service/mocks/service_mock.go
+++ b/manager/service/mocks/service_mock.go
@@ -225,6 +225,36 @@ func (mr *MockServiceMockRecorder) CreateConfig(arg0, arg1 interface{}) *gomock.
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateConfig", reflect.TypeOf((*MockService)(nil).CreateConfig), arg0, arg1)
 }
 
+// CreateModel mocks base method.
+func (m *MockService) CreateModel(arg0 context.Context, arg1 types.CreateModelParams, arg2 types.CreateModelRequest) (*types.Model, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CreateModel", arg0, arg1, arg2)
+	ret0, _ := ret[0].(*types.Model)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CreateModel indicates an expected call of CreateModel.
+func (mr *MockServiceMockRecorder) CreateModel(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateModel", reflect.TypeOf((*MockService)(nil).CreateModel), arg0, arg1, arg2)
+}
+
+// CreateModelVersion mocks base method.
+func (m *MockService) CreateModelVersion(arg0 context.Context, arg1 types.CreateModelVersionParams, arg2 types.CreateModelVersionRequest) (*types.ModelVersion, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CreateModelVersion", arg0, arg1, arg2)
+	ret0, _ := ret[0].(*types.ModelVersion)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CreateModelVersion indicates an expected call of CreateModelVersion.
+func (mr *MockServiceMockRecorder) CreateModelVersion(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateModelVersion", reflect.TypeOf((*MockService)(nil).CreateModelVersion), arg0, arg1, arg2)
+}
+
 // CreateOauth mocks base method.
 func (m *MockService) CreateOauth(arg0 context.Context, arg1 types.CreateOauthRequest) (*model.Oauth, error) {
 	m.ctrl.T.Helper()
@@ -488,6 +518,34 @@ func (mr *MockServiceMockRecorder) DestroyJob(arg0, arg1 interface{}) *gomock.Ca
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DestroyJob", reflect.TypeOf((*MockService)(nil).DestroyJob), arg0, arg1)
 }
 
+// DestroyModel mocks base method.
+func (m *MockService) DestroyModel(arg0 context.Context, arg1 types.ModelParams) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DestroyModel", arg0, arg1)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DestroyModel indicates an expected call of DestroyModel.
+func (mr *MockServiceMockRecorder) DestroyModel(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DestroyModel", reflect.TypeOf((*MockService)(nil).DestroyModel), arg0, arg1)
+}
+
+// DestroyModelVersion mocks base method.
+func (m *MockService) DestroyModelVersion(arg0 context.Context, arg1 types.ModelVersionParams) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DestroyModelVersion", arg0, arg1)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DestroyModelVersion indicates an expected call of DestroyModelVersion.
+func (mr *MockServiceMockRecorder) DestroyModelVersion(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DestroyModelVersion", reflect.TypeOf((*MockService)(nil).DestroyModelVersion), arg0, arg1)
+}
+
 // DestroyOauth mocks base method.
 func (m *MockService) DestroyOauth(arg0 context.Context, arg1 uint) error {
 	m.ctrl.T.Helper()
@@ -738,6 +796,66 @@ func (mr *MockServiceMockRecorder) GetJobs(arg0, arg1 interface{}) *gomock.Call
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobs", reflect.TypeOf((*MockService)(nil).GetJobs), arg0, arg1)
 }
 
+// GetModel mocks base method.
+func (m *MockService) GetModel(arg0 context.Context, arg1 types.ModelParams) (*types.Model, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModel", arg0, arg1)
+	ret0, _ := ret[0].(*types.Model)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetModel indicates an expected call of GetModel.
+func (mr *MockServiceMockRecorder) GetModel(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModel", reflect.TypeOf((*MockService)(nil).GetModel), arg0, arg1)
+}
+
+// GetModelVersion mocks base method.
+func (m *MockService) GetModelVersion(arg0 context.Context, arg1 types.ModelVersionParams) (*types.ModelVersion, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModelVersion", arg0, arg1)
+	ret0, _ := ret[0].(*types.ModelVersion)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetModelVersion indicates an expected call of GetModelVersion.
+func (mr *MockServiceMockRecorder) GetModelVersion(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModelVersion", reflect.TypeOf((*MockService)(nil).GetModelVersion), arg0, arg1)
+}
+
+// GetModelVersions mocks base method.
+func (m *MockService) GetModelVersions(arg0 context.Context, arg1 types.GetModelVersionsParams) ([]*types.ModelVersion, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModelVersions", arg0, arg1)
+	ret0, _ := ret[0].([]*types.ModelVersion)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetModelVersions indicates an expected call of GetModelVersions.
+func (mr *MockServiceMockRecorder) GetModelVersions(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModelVersions", reflect.TypeOf((*MockService)(nil).GetModelVersions), arg0, arg1)
+}
+
+// GetModels mocks base method.
+func (m *MockService) GetModels(arg0 context.Context, arg1 types.GetModelsParams) ([]*types.Model, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetModels", arg0, arg1)
+	ret0, _ := ret[0].([]*types.Model)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetModels indicates an expected call of GetModels.
+func (mr *MockServiceMockRecorder) GetModels(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModels", reflect.TypeOf((*MockService)(nil).GetModels), arg0, arg1)
+}
+
 // GetOauth mocks base method.
 func (m *MockService) GetOauth(arg0 context.Context, arg1 uint) (*model.Oauth, error) {
 	m.ctrl.T.Helper()
@@ -1192,6 +1310,36 @@ func (mr *MockServiceMockRecorder) UpdateJob(arg0, arg1, arg2 interface{}) *gomo
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateJob", reflect.TypeOf((*MockService)(nil).UpdateJob), arg0, arg1, arg2)
 }
 
+// UpdateModel mocks base method.
+func (m *MockService) UpdateModel(arg0 context.Context, arg1 types.ModelParams, arg2 types.UpdateModelRequest) (*types.Model, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateModel", arg0, arg1, arg2)
+	ret0, _ := ret[0].(*types.Model)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// UpdateModel indicates an expected call of UpdateModel.
+func (mr *MockServiceMockRecorder) UpdateModel(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateModel", reflect.TypeOf((*MockService)(nil).UpdateModel), arg0, arg1, arg2)
+}
+
+// UpdateModelVersion mocks base method.
+func (m *MockService) UpdateModelVersion(arg0 context.Context, arg1 types.ModelVersionParams, arg2 types.UpdateModelVersionRequest) (*types.ModelVersion, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateModelVersion", arg0, arg1, arg2)
+	ret0, _ := ret[0].(*types.ModelVersion)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// UpdateModelVersion indicates an expected call of UpdateModelVersion.
+func (mr *MockServiceMockRecorder) UpdateModelVersion(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateModelVersion", reflect.TypeOf((*MockService)(nil).UpdateModelVersion), arg0, arg1, arg2)
+}
+
 // UpdateOauth mocks base method.
 func (m *MockService) UpdateOauth(arg0 context.Context, arg1 uint, arg2 types.UpdateOauthRequest) (*model.Oauth, error) {
 	m.ctrl.T.Helper()
diff --git a/manager/service/model.go b/manager/service/model.go
new file mode 100644
index 00000000..26f1060a
--- /dev/null
+++ b/manager/service/model.go
@@ -0,0 +1,219 @@
+/*
+ *     Copyright 2022 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package service
+
+import (
+	"context"
+	"time"
+
+	"github.com/google/uuid"
+
+	"d7y.io/dragonfly/v2/manager/cache"
+	"d7y.io/dragonfly/v2/manager/model"
+	"d7y.io/dragonfly/v2/manager/types"
+)
+
+func (s *service) CreateModel(ctx context.Context, params types.CreateModelParams, json types.CreateModelRequest) (*types.Model, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	model := types.Model{
+		ID:          json.ID,
+		Name:        json.Name,
+		VersionID:   json.VersionID,
+		SchedulerID: json.SchedulerID,
+		Hostname:    json.Hostname,
+		IP:          json.IP,
+		CreatedAt:   time.Now(),
+		UpdatedAt:   time.Now(),
+	}
+
+	if _, err := s.rdb.Set(ctx, cache.MakeModelKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, model.ID), &model, 0).Result(); err != nil {
+		return nil, err
+	}
+
+	return &model, nil
+}
+
+func (s *service) DestroyModel(ctx context.Context, params types.ModelParams) error {
+	if _, err := s.GetModel(ctx, params); err != nil {
+		return err
+	}
+
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return err
+	}
+
+	if _, err := s.rdb.Del(ctx, cache.MakeModelKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ID)).Result(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *service) UpdateModel(ctx context.Context, params types.ModelParams, json types.UpdateModelRequest) (*types.Model, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	model, err := s.GetModel(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+	model.VersionID = json.VersionID
+	model.UpdatedAt = time.Now()
+
+	if _, err := s.rdb.Set(ctx, cache.MakeModelKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ID), model, 0).Result(); err != nil {
+		return nil, err
+	}
+
+	return model, nil
+}
+
+func (s *service) GetModel(ctx context.Context, params types.ModelParams) (*types.Model, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	var model types.Model
+	if err := s.rdb.Get(ctx, cache.MakeModelKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ID)).Scan(&model); err != nil {
+		return nil, err
+	}
+
+	return &model, nil
+}
+
+func (s *service) GetModels(ctx context.Context, params types.GetModelsParams) ([]*types.Model, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	models := []*types.Model{}
+	iter := s.rdb.Scan(ctx, 0, cache.MakeModelKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, "*"), 0).Iterator()
+	for iter.Next(ctx) {
+		var model types.Model
+		if err := s.rdb.Get(ctx, iter.Val()).Scan(&model); err != nil {
+			return nil, err
+		}
+
+		models = append(models, &model)
+	}
+
+	return models, nil
+}
+
+func (s *service) CreateModelVersion(ctx context.Context, params types.CreateModelVersionParams, json types.CreateModelVersionRequest) (*types.ModelVersion, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	modelVersion := types.ModelVersion{
+		ID:        uuid.New().String(),
+		Precision: json.Precision,
+		Recall:    json.Recall,
+		Data:      json.Data,
+		CreatedAt: time.Now(),
+		UpdatedAt: time.Now(),
+	}
+
+	if _, err := s.rdb.Set(ctx, cache.MakeModelVersionKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ModelID, modelVersion.ID), &modelVersion, 0).Result(); err != nil {
+		return nil, err
+	}
+
+	return &modelVersion, nil
+}
+
+func (s *service) DestroyModelVersion(ctx context.Context, params types.ModelVersionParams) error {
+	if _, err := s.GetModelVersion(ctx, params); err != nil {
+		return err
+	}
+
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return err
+	}
+
+	if _, err := s.rdb.Del(ctx, cache.MakeModelVersionKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ModelID, params.ID)).Result(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (s *service) UpdateModelVersion(ctx context.Context, params types.ModelVersionParams, json types.UpdateModelVersionRequest) (*types.ModelVersion, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	modelVersion, err := s.GetModelVersion(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+
+	modelVersion.Precision = json.Precision
+	modelVersion.Recall = json.Recall
+	modelVersion.Data = json.Data
+	modelVersion.UpdatedAt = time.Now()
+
+	if _, err := s.rdb.Set(ctx, cache.MakeModelVersionKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ModelID, modelVersion.ID), modelVersion, 0).Result(); err != nil {
+		return nil, err
+	}
+
+	return modelVersion, nil
+}
+
+func (s *service) GetModelVersion(ctx context.Context, params types.ModelVersionParams) (*types.ModelVersion, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	var modelVersion types.ModelVersion
+	if err := s.rdb.Get(ctx, cache.MakeModelVersionKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ModelID, params.ID)).Scan(&modelVersion); err != nil {
+		return nil, err
+	}
+
+	return &modelVersion, nil
+}
+
+func (s *service) GetModelVersions(ctx context.Context, params types.GetModelVersionsParams) ([]*types.ModelVersion, error) {
+	scheduler := model.Scheduler{}
+	if err := s.db.WithContext(ctx).First(&scheduler, params.SchedulerID).Error; err != nil {
+		return nil, err
+	}
+
+	modelVersions := []*types.ModelVersion{}
+	iter := s.rdb.Scan(ctx, 0, cache.MakeModelVersionKey(scheduler.SchedulerClusterID, scheduler.HostName, scheduler.IP, params.ModelID, "*"), 0).Iterator()
+	for iter.Next(ctx) {
+		var modelVersion types.ModelVersion
+		if err := s.rdb.Get(ctx, iter.Val()).Scan(&modelVersion); err != nil {
+			return nil, err
+		}
+
+		modelVersions = append(modelVersions, &modelVersion)
+	}
+
+	return modelVersions, nil
+}
diff --git a/manager/service/service.go b/manager/service/service.go
index 6180ca38..cb31bef5 100644
--- a/manager/service/service.go
+++ b/manager/service/service.go
@@ -137,6 +137,18 @@ type Service interface {
 	DeleteSchedulerClusterToApplication(context.Context, uint, uint) error
 	AddSeedPeerClusterToApplication(context.Context, uint, uint) error
 	DeleteSeedPeerClusterToApplication(context.Context, uint, uint) error
+
+	CreateModel(context.Context, types.CreateModelParams, types.CreateModelRequest) (*types.Model, error)
+	DestroyModel(context.Context, types.ModelParams) error
+	UpdateModel(context.Context, types.ModelParams, types.UpdateModelRequest) (*types.Model, error)
+	GetModel(context.Context, types.ModelParams) (*types.Model, error)
+	GetModels(context.Context, types.GetModelsParams) ([]*types.Model, error)
+
+	CreateModelVersion(context.Context, types.CreateModelVersionParams, types.CreateModelVersionRequest) (*types.ModelVersion, error)
+	DestroyModelVersion(context.Context, types.ModelVersionParams) error
+	UpdateModelVersion(context.Context, types.ModelVersionParams, types.UpdateModelVersionRequest) (*types.ModelVersion, error)
+	GetModelVersion(context.Context, types.ModelVersionParams) (*types.ModelVersion, error)
+	GetModelVersions(context.Context, types.GetModelVersionsParams) ([]*types.ModelVersion, error)
 }
 
 type service struct {
diff --git a/manager/types/model.go b/manager/types/model.go
new file mode 100644
index 00000000..267f0873
--- /dev/null
+++ b/manager/types/model.go
@@ -0,0 +1,117 @@
+/*
+ *     Copyright 2022 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package types
+
+import (
+	"encoding/json"
+	"time"
+)
+
+const (
+	// ModelIDEvaluator is id of the evaluation model.
+	ModelIDEvaluator = "evaluator"
+)
+
+type ModelParams struct {
+	SchedulerID uint   `uri:"id" binding:"required"`
+	ID          string `uri:"model_id" binding:"required"`
+}
+
+type CreateModelParams struct {
+	SchedulerID uint `uri:"id" binding:"required"`
+}
+
+type CreateModelRequest struct {
+	ID          string `json:"id" binding:"required,oneof=evaluator"`
+	Name        string `json:"name" binding:"required"`
+	VersionID   string `json:"version_id" binding:"required"`
+	SchedulerID uint   `json:"scheduler_id" binding:"required"`
+	Hostname    string `json:"hostname" binding:"required"`
+	IP          string `json:"ip" binding:"required"`
+}
+
+type GetModelsParams struct {
+	SchedulerID uint `uri:"id" binding:"required"`
+}
+
+type UpdateModelRequest struct {
+	VersionID string `json:"version_id" binding:"omitempty"`
+}
+
+type Model struct {
+	ID          string    `json:"id" binding:"required"`
+	Name        string    `json:"name" binding:"required"`
+	VersionID   string    `json:"version_id" binding:"required"`
+	SchedulerID uint      `json:"scheduler_id" binding:"required"`
+	Hostname    string    `json:"hostname" binding:"required"`
+	IP          string    `json:"ip" binding:"required"`
+	CreatedAt   time.Time `json:"create_at" binding:"required"`
+	UpdatedAt   time.Time `json:"updated_at" binding:"required"`
+}
+
+func (m *Model) MarshalBinary() ([]byte, error) {
+	return json.Marshal(m)
+}
+
+func (m *Model) UnmarshalBinary(data []byte) error {
+	return json.Unmarshal(data, m)
+}
+
+type ModelVersionParams struct {
+	SchedulerID uint   `uri:"id" binding:"required"`
+	ModelID     string `uri:"model_id" binding:"required"`
+	ID          string `uri:"version_id" binding:"required"`
+}
+
+type CreateModelVersionParams struct {
+	SchedulerID uint   `uri:"id" binding:"required"`
+	ModelID     string `uri:"model_id" binding:"required"`
+}
+
+type CreateModelVersionRequest struct {
+	Precision float64 `json:"precision" binding:"required"`
+	Recall    float64 `json:"recall" binding:"required"`
+	Data      []byte  `json:"data" binding:"required"`
+}
+
+type GetModelVersionsParams struct {
+	SchedulerID uint   `uri:"id" binding:"required"`
+	ModelID     string `uri:"model_id" binding:"required"`
+}
+
+type UpdateModelVersionRequest struct {
+	Precision float64 `json:"precision" binding:"omitempty"`
+	Recall    float64 `json:"recall" binding:"omitempty"`
+	Data      []byte  `json:"data" binding:"omitempty"`
+}
+
+type ModelVersion struct {
+	ID        string    `json:"id" binding:"required"`
+	Precision float64   `json:"precision" binding:"required"`
+	Recall    float64   `json:"recall" binding:"required"`
+	Data      []byte    `json:"data" binding:"required"`
+	CreatedAt time.Time `json:"create_at" binding:"required"`
+	UpdatedAt time.Time `json:"updated_at" binding:"required"`
+}
+
+func (mv *ModelVersion) MarshalBinary() ([]byte, error) {
+	return json.Marshal(mv)
+}
+
+func (mv *ModelVersion) UnmarshalBinary(data []byte) error {
+	return json.Unmarshal(data, mv)
+}


Test patch:
diff --git a/manager/config/config_test.go b/manager/config/config_test.go
index 0c592eff..05e35000 100644
--- a/manager/config/config_test.go
+++ b/manager/config/config_test.go
@@ -76,7 +76,7 @@ func TestManagerConfig_Load(t *testing.T) {
 				Host:      "bar",
 				Password:  "bar",
 				Port:      6379,
-				CacheDB:   0,
+				DB:        0,
 				BrokerDB:  1,
 				BackendDB: 2,
 			},
diff --git a/manager/config/testdata/manager.yaml b/manager/config/testdata/manager.yaml
index 2752003f..74f05e75 100644
--- a/manager/config/testdata/manager.yaml
+++ b/manager/config/testdata/manager.yaml
@@ -37,7 +37,7 @@ database:
     password: bar
     host: bar
     port: 6379
-    cacheDB: 0
+    db: 0
     brokerDB: 1
     backendDB: 2