feat: custom app icon (#7196)

Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
Hash Brown
2024-08-19 09:16:33 +08:00
committed by GitHub
parent a0c689c273
commit fbf31b5d52
65 changed files with 1068 additions and 352 deletions

View File

@@ -61,6 +61,7 @@ class AppListApi(Resource):
parser.add_argument('name', type=str, required=True, location='json')
parser.add_argument('description', type=str, location='json')
parser.add_argument('mode', type=str, choices=ALLOW_CREATE_APP_MODES, location='json')
parser.add_argument('icon_type', type=str, location='json')
parser.add_argument('icon', type=str, location='json')
parser.add_argument('icon_background', type=str, location='json')
args = parser.parse_args()
@@ -94,6 +95,7 @@ class AppImportApi(Resource):
parser.add_argument('data', type=str, required=True, nullable=False, location='json')
parser.add_argument('name', type=str, location='json')
parser.add_argument('description', type=str, location='json')
parser.add_argument('icon_type', type=str, location='json')
parser.add_argument('icon', type=str, location='json')
parser.add_argument('icon_background', type=str, location='json')
args = parser.parse_args()
@@ -167,6 +169,7 @@ class AppApi(Resource):
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, required=True, nullable=False, location='json')
parser.add_argument('description', type=str, location='json')
parser.add_argument('icon_type', type=str, location='json')
parser.add_argument('icon', type=str, location='json')
parser.add_argument('icon_background', type=str, location='json')
parser.add_argument('max_active_requests', type=int, location='json')
@@ -208,6 +211,7 @@ class AppCopyApi(Resource):
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, location='json')
parser.add_argument('description', type=str, location='json')
parser.add_argument('icon_type', type=str, location='json')
parser.add_argument('icon', type=str, location='json')
parser.add_argument('icon_background', type=str, location='json')
args = parser.parse_args()

View File

@@ -16,6 +16,7 @@ from models.model import Site
def parse_app_site_args():
parser = reqparse.RequestParser()
parser.add_argument('title', type=str, required=False, location='json')
parser.add_argument('icon_type', type=str, required=False, location='json')
parser.add_argument('icon', type=str, required=False, location='json')
parser.add_argument('icon_background', type=str, required=False, location='json')
parser.add_argument('description', type=str, required=False, location='json')
@@ -53,6 +54,7 @@ class AppSite(Resource):
for attr_name in [
'title',
'icon_type',
'icon',
'icon_background',
'description',

View File

@@ -459,6 +459,7 @@ class ConvertToWorkflowApi(Resource):
if request.data:
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, required=False, nullable=True, location='json')
parser.add_argument('icon_type', type=str, required=False, nullable=True, location='json')
parser.add_argument('icon', type=str, required=False, nullable=True, location='json')
parser.add_argument('icon_background', type=str, required=False, nullable=True, location='json')
args = parser.parse_args()

View File

@@ -6,6 +6,7 @@ from configs import dify_config
from controllers.web import api
from controllers.web.wraps import WebApiResource
from extensions.ext_database import db
from libs.helper import AppIconUrlField
from models.account import TenantStatus
from models.model import Site
from services.feature_service import FeatureService
@@ -28,8 +29,10 @@ class AppSiteApi(WebApiResource):
'title': fields.String,
'chat_color_theme': fields.String,
'chat_color_theme_inverted': fields.Boolean,
'icon_type': fields.String,
'icon': fields.String,
'icon_background': fields.String,
'icon_url': AppIconUrlField,
'description': fields.String,
'copyright': fields.String,
'privacy_policy': fields.String,

View File

@@ -11,6 +11,7 @@ def handle(sender, **kwargs):
site = Site(
app_id=app.id,
title=app.name,
icon_type=app.icon_type,
icon=app.icon,
icon_background=app.icon_background,
default_language=account.interface_language,

View File

@@ -1,14 +1,16 @@
from flask_restful import fields
from libs.helper import TimestampField
from libs.helper import AppIconUrlField, TimestampField
app_detail_kernel_fields = {
"id": fields.String,
"name": fields.String,
"description": fields.String,
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
}
related_app_list = {
@@ -71,8 +73,10 @@ app_partial_fields = {
"max_active_requests": fields.Raw(),
"description": fields.String(attribute="desc_or_prompt"),
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
"model_config": fields.Nested(model_config_partial_fields, attribute="app_model_config", allow_null=True),
"created_at": TimestampField,
"tags": fields.List(fields.Nested(tag_fields)),
@@ -104,8 +108,10 @@ site_fields = {
"access_token": fields.String(attribute="code"),
"code": fields.String,
"title": fields.String,
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
"description": fields.String,
"default_language": fields.String,
"chat_color_theme": fields.String,
@@ -125,8 +131,10 @@ app_detail_fields_with_site = {
"name": fields.String,
"description": fields.String,
"mode": fields.String(attribute="mode_compatible_with_agent"),
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
"enable_site": fields.Boolean,
"enable_api": fields.Boolean,
"model_config": fields.Nested(model_config_fields, attribute="app_model_config", allow_null=True),

View File

@@ -1,13 +1,15 @@
from flask_restful import fields
from libs.helper import TimestampField
from libs.helper import AppIconUrlField, TimestampField
app_fields = {
"id": fields.String,
"name": fields.String,
"mode": fields.String,
"icon_type": fields.String,
"icon": fields.String,
"icon_background": fields.String,
"icon_url": AppIconUrlField,
}
installed_app_fields = {

View File

@@ -16,6 +16,7 @@ from flask import Response, current_app, stream_with_context
from flask_restful import fields
from core.app.features.rate_limiting.rate_limit import RateLimitGenerator
from core.file.upload_file_parser import UploadFileParser
from extensions.ext_redis import redis_client
from models.account import Account
@@ -24,6 +25,18 @@ def run(script):
return subprocess.getstatusoutput("source /root/.bashrc && " + script)
class AppIconUrlField(fields.Raw):
def output(self, key, obj):
if obj is None:
return None
from models.model import IconType
if obj.icon_type == IconType.IMAGE.value:
return UploadFileParser.get_signed_temp_image_url(obj.icon)
return None
class TimestampField(fields.Raw):
def format(self, value) -> int:
return int(value.timestamp())

View File

@@ -0,0 +1,39 @@
"""app and site icon type
Revision ID: a6be81136580
Revises: 8782057ff0dc
Create Date: 2024-08-15 10:01:24.697888
"""
import sqlalchemy as sa
from alembic import op
import models as models
# revision identifiers, used by Alembic.
revision = 'a6be81136580'
down_revision = '8782057ff0dc'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('apps', schema=None) as batch_op:
batch_op.add_column(sa.Column('icon_type', sa.String(length=255), nullable=True))
with op.batch_alter_table('sites', schema=None) as batch_op:
batch_op.add_column(sa.Column('icon_type', sa.String(length=255), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('sites', schema=None) as batch_op:
batch_op.drop_column('icon_type')
with op.batch_alter_table('apps', schema=None) as batch_op:
batch_op.drop_column('icon_type')
# ### end Alembic commands ###

View File

@@ -51,6 +51,10 @@ class AppMode(Enum):
raise ValueError(f'invalid mode value {value}')
class IconType(Enum):
IMAGE = "image"
EMOJI = "emoji"
class App(db.Model):
__tablename__ = 'apps'
__table_args__ = (
@@ -63,6 +67,7 @@ class App(db.Model):
name = db.Column(db.String(255), nullable=False)
description = db.Column(db.Text, nullable=False, server_default=db.text("''::character varying"))
mode = db.Column(db.String(255), nullable=False)
icon_type = db.Column(db.String(255), nullable=True)
icon = db.Column(db.String(255))
icon_background = db.Column(db.String(255))
app_model_config_id = db.Column(StringUUID, nullable=True)
@@ -1087,6 +1092,7 @@ class Site(db.Model):
id = db.Column(StringUUID, server_default=db.text('uuid_generate_v4()'))
app_id = db.Column(StringUUID, nullable=False)
title = db.Column(db.String(255), nullable=False)
icon_type = db.Column(db.String(255), nullable=True)
icon = db.Column(db.String(255))
icon_background = db.Column(db.String(255))
description = db.Column(db.Text)

View File

@@ -82,6 +82,7 @@ class AppDslService:
# get app basic info
name = args.get("name") if args.get("name") else app_data.get('name')
description = args.get("description") if args.get("description") else app_data.get('description', '')
icon_type = args.get("icon_type") if args.get("icon_type") else app_data.get('icon_type')
icon = args.get("icon") if args.get("icon") else app_data.get('icon')
icon_background = args.get("icon_background") if args.get("icon_background") \
else app_data.get('icon_background')
@@ -96,6 +97,7 @@ class AppDslService:
account=account,
name=name,
description=description,
icon_type=icon_type,
icon=icon,
icon_background=icon_background
)
@@ -107,6 +109,7 @@ class AppDslService:
account=account,
name=name,
description=description,
icon_type=icon_type,
icon=icon,
icon_background=icon_background
)
@@ -165,8 +168,8 @@ class AppDslService:
"app": {
"name": app_model.name,
"mode": app_model.mode,
"icon": app_model.icon,
"icon_background": app_model.icon_background,
"icon": '🤖' if app_model.icon_type == 'image' else app_model.icon,
"icon_background": '#FFEAD5' if app_model.icon_type == 'image' else app_model.icon_background,
"description": app_model.description
}
}
@@ -207,6 +210,7 @@ class AppDslService:
account: Account,
name: str,
description: str,
icon_type: str,
icon: str,
icon_background: str) -> App:
"""
@@ -218,6 +222,7 @@ class AppDslService:
:param account: Account instance
:param name: app name
:param description: app description
:param icon_type: app icon type, "emoji" or "image"
:param icon: app icon
:param icon_background: app icon background
"""
@@ -231,6 +236,7 @@ class AppDslService:
account=account,
name=name,
description=description,
icon_type=icon_type,
icon=icon,
icon_background=icon_background
)
@@ -307,6 +313,7 @@ class AppDslService:
account: Account,
name: str,
description: str,
icon_type: str,
icon: str,
icon_background: str) -> App:
"""
@@ -331,6 +338,7 @@ class AppDslService:
account=account,
name=name,
description=description,
icon_type=icon_type,
icon=icon,
icon_background=icon_background
)
@@ -358,6 +366,7 @@ class AppDslService:
account: Account,
name: str,
description: str,
icon_type: str,
icon: str,
icon_background: str) -> App:
"""
@@ -368,6 +377,7 @@ class AppDslService:
:param account: Account instance
:param name: app name
:param description: app description
:param icon_type: app icon type, "emoji" or "image"
:param icon: app icon
:param icon_background: app icon background
"""
@@ -376,6 +386,7 @@ class AppDslService:
mode=app_mode.value,
name=name,
description=description,
icon_type=icon_type,
icon=icon,
icon_background=icon_background,
enable_site=True,

View File

@@ -119,6 +119,7 @@ class AppService:
app.name = args['name']
app.description = args.get('description', '')
app.mode = args['mode']
app.icon_type = args.get('icon_type', 'emoji')
app.icon = args['icon']
app.icon_background = args['icon_background']
app.tenant_id = tenant_id
@@ -210,6 +211,7 @@ class AppService:
app.name = args.get('name')
app.description = args.get('description', '')
app.max_active_requests = args.get('max_active_requests')
app.icon_type = args.get('icon_type', 'emoji')
app.icon = args.get('icon')
app.icon_background = args.get('icon_background')
app.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)

View File

@@ -35,6 +35,7 @@ class WorkflowConverter:
def convert_to_workflow(self, app_model: App,
account: Account,
name: str,
icon_type: str,
icon: str,
icon_background: str) -> App:
"""
@@ -50,6 +51,7 @@ class WorkflowConverter:
:param account: Account
:param name: new app name
:param icon: new app icon
:param icon_type: new app icon type
:param icon_background: new app icon background
:return: new App instance
"""
@@ -66,6 +68,7 @@ class WorkflowConverter:
new_app.name = name if name else app_model.name + '(workflow)'
new_app.mode = AppMode.ADVANCED_CHAT.value \
if app_model.mode == AppMode.CHAT.value else AppMode.WORKFLOW.value
new_app.icon_type = icon_type if icon_type else app_model.icon_type
new_app.icon = icon if icon else app_model.icon
new_app.icon_background = icon_background if icon_background else app_model.icon_background
new_app.enable_site = app_model.enable_site

View File

@@ -302,6 +302,7 @@ class WorkflowService:
app_model=app_model,
account=account,
name=args.get('name'),
icon_type=args.get('icon_type'),
icon=args.get('icon'),
icon_background=args.get('icon_background'),
)