Skip to content

Commit 3609ae5

Browse files
authored
feat(mlops): add modal deployment (#548)
* add modal deployment * able to save modal information
1 parent 9e79a37 commit 3609ae5

File tree

5 files changed

+120
-44
lines changed

5 files changed

+120
-44
lines changed

api_docs.yml

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,7 @@ paths:
25112511
name: project_type
25122512
type: string
25132513
description: Shows the type of project. Corresponds to the predifined types like Research, Student, Personal, Commercial or Other.
2514-
2514+
- in: query
25152515
name: series
25162516
type: boolean
25172517
description: Returns graph data when set to true
@@ -2782,6 +2782,55 @@ paths:
27822782
500:
27832783
description: "Internal Server Error"
27842784

2785+
"/projects/{project_id}/apps/ml":
2786+
post:
2787+
tags:
2788+
- apps
2789+
consumes:
2790+
- application/json
2791+
parameters:
2792+
- in: header
2793+
name: Authorization
2794+
required: true
2795+
- in: path
2796+
name: project_id
2797+
required: true
2798+
type: string
2799+
- in: body
2800+
name: project
2801+
schema:
2802+
required:
2803+
- name
2804+
- is_notebook
2805+
properties:
2806+
name:
2807+
type: string
2808+
is_notebook:
2809+
type: boolean
2810+
is_modal:
2811+
type: boolean
2812+
example: false
2813+
model_image_uri:
2814+
type: string
2815+
example: ""
2816+
api_type:
2817+
type: string
2818+
example: "REST"
2819+
model_server:
2820+
type: string
2821+
example: "MLFLOW_SERVER"
2822+
produces:
2823+
- application/json
2824+
responses:
2825+
201:
2826+
description: "Success"
2827+
400:
2828+
description: "Bad request"
2829+
409:
2830+
description: "Error"
2831+
500:
2832+
description: "Internal Server Error"
2833+
27852834
"/projects/{project_id}/billing/info":
27862835
post:
27872836
tags:
@@ -3267,44 +3316,6 @@ paths:
32673316
500:
32683317
description: "Internal Server Error"
32693318

3270-
"/projects/{project_id}/apps/ml":
3271-
post:
3272-
tags:
3273-
- apps
3274-
consumes:
3275-
- application/json
3276-
parameters:
3277-
- in: header
3278-
name: Authorization
3279-
required: true
3280-
- in: path
3281-
name: project_id
3282-
required: true
3283-
type: string
3284-
- in: body
3285-
name: project
3286-
schema:
3287-
required:
3288-
- name
3289-
- is_notebook
3290-
properties:
3291-
name:
3292-
type: string
3293-
is_notebook:
3294-
type: boolean
3295-
3296-
produces:
3297-
- application/json
3298-
responses:
3299-
201:
3300-
description: "Success"
3301-
400:
3302-
description: "Bad request"
3303-
409:
3304-
description: "Error"
3305-
500:
3306-
description: "Internal Server Error"
3307-
33083319
"/apps/{app_id}":
33093320
get:
33103321
tags:

app/controllers/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,7 @@ def post(self, project_id):
480480
project_data, errors = project_schema.dumps(project)
481481
cluster_data, errors = cluster_schema.dumps(cluster)
482482
data = dict(
483-
name=validated_app_data.get('name', None),
484-
is_notebook=validated_app_data.get('is_notebook', True),
483+
**validated_app_data,
485484
project=json.loads(project_data),
486485
# user=user
487486
cluster=json.loads(cluster_data)
@@ -501,6 +500,7 @@ def post(self, project_id):
501500
return dict(status='fail', message=response.json().get('message')), response.status_code
502501

503502
response_data = response.json().get('app', {})
503+
504504
new_app = App(**response_data)
505505

506506
saved_app = new_app.save()

app/models/app.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class App(ModelMixin):
1313
id = db.Column(UUID(as_uuid=True), primary_key=True,
1414
server_default=sa_text("uuid_generate_v4()"))
1515
name = db.Column(db.String(256), nullable=True)
16-
image = db.Column(db.String(256), nullable=False)
16+
image = db.Column(db.String(256), nullable=True)
1717
project_id = db.Column(UUID(as_uuid=True), db.ForeignKey(
1818
'project.id'), nullable=False)
1919
url = db.Column(db.String(256), nullable=True)
@@ -31,3 +31,7 @@ class App(ModelMixin):
3131
"AppState", backref='app_state', lazy=True)
3232
is_ai = db.Column(db.Boolean, default=False)
3333
is_notebook = db.Column(db.Boolean, default=False)
34+
is_modal = db.Column(db.Boolean, default=False)
35+
model_image_uri = db.Column(db.String(256), nullable=True)
36+
api_type = db.Column(db.String(256), nullable=True)
37+
model_server = db.Column(db.String(256), nullable=True)

app/schemas/app.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ class AppSchema(Schema):
88

99
id = fields.String(dump_only=True)
1010

11-
name = fields.String(required=True, error_messgae={
11+
name = fields.String(required=True, error_messages={
1212
"required": "name is required"},
1313
validate=[
1414
validate.Regexp(
1515
regex=r'^(?!\s*$)', error='name should be a valid string'
1616
),
1717
])
18-
image = fields.String(required=True, error_message={
18+
image = fields.String(required=True, error_messages={
1919
"required": "image is required"},
2020
validate=[
2121
validate.Regexp(
@@ -47,6 +47,10 @@ class AppSchema(Schema):
4747
app_status = fields.Nested(AppStateSchema, many=True, dump_only=True)
4848
is_ai = fields.Boolean(required=False)
4949
is_notebook = fields.Boolean(required=False)
50+
is_modal = fields.Bool(required=False)
51+
model_image_uri = fields.Str(required=False)
52+
api_type = fields.Str(required=False)
53+
model_server = fields.Str(required=False)
5054

5155
def get_age(self, obj):
5256
return get_item_age(obj.date_created)
@@ -79,3 +83,20 @@ class AppDeploySchema(AppSchema):
7983
class MLAppDeploySchema(Schema):
8084
name = fields.String(required=True)
8185
is_notebook = fields.Boolean(required=True)
86+
is_modal = fields.Bool(required=False)
87+
model_image_uri = fields.Str(required=False)
88+
api_type = fields.Str(required=False, default="REST", validate=validate.OneOf(
89+
["REST", "GRPC"]
90+
))
91+
model_server = fields.Str(required=False, default="MLFLOW_SERVER", validate=validate.OneOf(
92+
[
93+
"SKLEARN_SERVER",
94+
"TENSORFLOW_SERVER",
95+
"XGBOOST_SERVER",
96+
"MLFLOW_SERVER",
97+
"TRITON_SERVER",
98+
"TEMPO_SERVER",
99+
"HUGGINGFACE_SERVER",
100+
"CUSTOM_INFERENCE_SERVER"
101+
]
102+
))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""empty message
2+
3+
Revision ID: 701f36199305
4+
Revises: 1bf24f39f11e
5+
Create Date: 2025-03-05 03:27:46.282625
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '701f36199305'
14+
down_revision = '1bf24f39f11e'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column('app', sa.Column('api_type', sa.String(length=256), nullable=True))
22+
op.add_column('app', sa.Column('is_modal', sa.Boolean(), nullable=True))
23+
op.add_column('app', sa.Column('model_image_uri', sa.String(length=256), nullable=True))
24+
op.add_column('app', sa.Column('model_server', sa.String(length=256), nullable=True))
25+
op.alter_column('app', 'image',
26+
existing_type=sa.VARCHAR(length=256),
27+
nullable=True)
28+
# ### end Alembic commands ###
29+
30+
31+
def downgrade():
32+
# ### commands auto generated by Alembic - please adjust! ###
33+
op.alter_column('app', 'image',
34+
existing_type=sa.VARCHAR(length=256),
35+
nullable=False)
36+
op.drop_column('app', 'model_server')
37+
op.drop_column('app', 'model_image_uri')
38+
op.drop_column('app', 'is_modal')
39+
op.drop_column('app', 'api_type')
40+
# ### end Alembic commands ###

0 commit comments

Comments
 (0)