mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
feat: allow users to use the app icon as the answer icon (#7888)
Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
parent
6f33351eb3
commit
80aa7c4019
|
@ -174,6 +174,7 @@ class AppApi(Resource):
|
||||||
parser.add_argument("icon", type=str, location="json")
|
parser.add_argument("icon", type=str, location="json")
|
||||||
parser.add_argument("icon_background", type=str, location="json")
|
parser.add_argument("icon_background", type=str, location="json")
|
||||||
parser.add_argument("max_active_requests", type=int, location="json")
|
parser.add_argument("max_active_requests", type=int, location="json")
|
||||||
|
parser.add_argument("use_icon_as_answer_icon", type=bool, location="json")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
app_service = AppService()
|
app_service = AppService()
|
||||||
|
|
|
@ -34,6 +34,7 @@ def parse_app_site_args():
|
||||||
)
|
)
|
||||||
parser.add_argument("prompt_public", type=bool, required=False, location="json")
|
parser.add_argument("prompt_public", type=bool, required=False, location="json")
|
||||||
parser.add_argument("show_workflow_steps", type=bool, required=False, location="json")
|
parser.add_argument("show_workflow_steps", type=bool, required=False, location="json")
|
||||||
|
parser.add_argument("use_icon_as_answer_icon", type=bool, required=False, location="json")
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ class AppSite(Resource):
|
||||||
"customize_token_strategy",
|
"customize_token_strategy",
|
||||||
"prompt_public",
|
"prompt_public",
|
||||||
"show_workflow_steps",
|
"show_workflow_steps",
|
||||||
|
"use_icon_as_answer_icon",
|
||||||
]:
|
]:
|
||||||
value = args.get(attr_name)
|
value = args.get(attr_name)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
|
|
@ -39,6 +39,7 @@ class AppSiteApi(WebApiResource):
|
||||||
"default_language": fields.String,
|
"default_language": fields.String,
|
||||||
"prompt_public": fields.Boolean,
|
"prompt_public": fields.Boolean,
|
||||||
"show_workflow_steps": fields.Boolean,
|
"show_workflow_steps": fields.Boolean,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
app_fields = {
|
app_fields = {
|
||||||
|
|
|
@ -58,6 +58,7 @@ app_detail_fields = {
|
||||||
"model_config": fields.Nested(model_config_fields, attribute="app_model_config", allow_null=True),
|
"model_config": fields.Nested(model_config_fields, attribute="app_model_config", allow_null=True),
|
||||||
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
||||||
"tracing": fields.Raw,
|
"tracing": fields.Raw,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
"created_by": fields.String,
|
"created_by": fields.String,
|
||||||
"created_at": TimestampField,
|
"created_at": TimestampField,
|
||||||
"updated_by": fields.String,
|
"updated_by": fields.String,
|
||||||
|
@ -91,6 +92,7 @@ app_partial_fields = {
|
||||||
"icon_url": AppIconUrlField,
|
"icon_url": AppIconUrlField,
|
||||||
"model_config": fields.Nested(model_config_partial_fields, attribute="app_model_config", allow_null=True),
|
"model_config": fields.Nested(model_config_partial_fields, attribute="app_model_config", allow_null=True),
|
||||||
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
"created_by": fields.String,
|
"created_by": fields.String,
|
||||||
"created_at": TimestampField,
|
"created_at": TimestampField,
|
||||||
"updated_by": fields.String,
|
"updated_by": fields.String,
|
||||||
|
@ -140,6 +142,7 @@ site_fields = {
|
||||||
"prompt_public": fields.Boolean,
|
"prompt_public": fields.Boolean,
|
||||||
"app_base_url": fields.String,
|
"app_base_url": fields.String,
|
||||||
"show_workflow_steps": fields.Boolean,
|
"show_workflow_steps": fields.Boolean,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
"created_by": fields.String,
|
"created_by": fields.String,
|
||||||
"created_at": TimestampField,
|
"created_at": TimestampField,
|
||||||
"updated_by": fields.String,
|
"updated_by": fields.String,
|
||||||
|
@ -161,6 +164,7 @@ app_detail_fields_with_site = {
|
||||||
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
"workflow": fields.Nested(workflow_partial_fields, allow_null=True),
|
||||||
"site": fields.Nested(site_fields),
|
"site": fields.Nested(site_fields),
|
||||||
"api_base_url": fields.String,
|
"api_base_url": fields.String,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
"created_by": fields.String,
|
"created_by": fields.String,
|
||||||
"created_at": TimestampField,
|
"created_at": TimestampField,
|
||||||
"updated_by": fields.String,
|
"updated_by": fields.String,
|
||||||
|
@ -184,4 +188,5 @@ app_site_fields = {
|
||||||
"customize_token_strategy": fields.String,
|
"customize_token_strategy": fields.String,
|
||||||
"prompt_public": fields.Boolean,
|
"prompt_public": fields.Boolean,
|
||||||
"show_workflow_steps": fields.Boolean,
|
"show_workflow_steps": fields.Boolean,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ app_fields = {
|
||||||
"icon": fields.String,
|
"icon": fields.String,
|
||||||
"icon_background": fields.String,
|
"icon_background": fields.String,
|
||||||
"icon_url": AppIconUrlField,
|
"icon_url": AppIconUrlField,
|
||||||
|
"use_icon_as_answer_icon": fields.Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
installed_app_fields = {
|
installed_app_fields = {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""add use_icon_as_answer_icon fields for app and site
|
||||||
|
|
||||||
|
Revision ID: 030f4915f36a
|
||||||
|
Revises: d0187d6a88dd
|
||||||
|
Create Date: 2024-09-01 12:55:45.129687
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
import models as models
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "030f4915f36a"
|
||||||
|
down_revision = "d0187d6a88dd"
|
||||||
|
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("use_icon_as_answer_icon", sa.Boolean(), server_default=sa.text("false"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
with op.batch_alter_table("sites", schema=None) as batch_op:
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column("use_icon_as_answer_icon", sa.Boolean(), server_default=sa.text("false"), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
# ### 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("use_icon_as_answer_icon")
|
||||||
|
|
||||||
|
with op.batch_alter_table("apps", schema=None) as batch_op:
|
||||||
|
batch_op.drop_column("use_icon_as_answer_icon")
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
|
@ -86,6 +86,7 @@ class App(db.Model):
|
||||||
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
|
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
|
||||||
updated_by = db.Column(StringUUID, nullable=True)
|
updated_by = db.Column(StringUUID, nullable=True)
|
||||||
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
|
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
|
||||||
|
use_icon_as_answer_icon = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def desc_or_prompt(self):
|
def desc_or_prompt(self):
|
||||||
|
@ -1114,6 +1115,7 @@ class Site(db.Model):
|
||||||
copyright = db.Column(db.String(255))
|
copyright = db.Column(db.String(255))
|
||||||
privacy_policy = db.Column(db.String(255))
|
privacy_policy = db.Column(db.String(255))
|
||||||
show_workflow_steps = db.Column(db.Boolean, nullable=False, server_default=db.text('true'))
|
show_workflow_steps = db.Column(db.Boolean, nullable=False, server_default=db.text('true'))
|
||||||
|
use_icon_as_answer_icon = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
|
||||||
custom_disclaimer = db.Column(db.String(255), nullable=True)
|
custom_disclaimer = db.Column(db.String(255), nullable=True)
|
||||||
customize_domain = db.Column(db.String(255))
|
customize_domain = db.Column(db.String(255))
|
||||||
customize_token_strategy = db.Column(db.String(255), nullable=False)
|
customize_token_strategy = db.Column(db.String(255), nullable=False)
|
||||||
|
|
|
@ -87,6 +87,7 @@ class AppDslService:
|
||||||
icon_background = (
|
icon_background = (
|
||||||
args.get("icon_background") if args.get("icon_background") else app_data.get("icon_background")
|
args.get("icon_background") if args.get("icon_background") else app_data.get("icon_background")
|
||||||
)
|
)
|
||||||
|
use_icon_as_answer_icon = app_data.get("use_icon_as_answer_icon", False)
|
||||||
|
|
||||||
# import dsl and create app
|
# import dsl and create app
|
||||||
app_mode = AppMode.value_of(app_data.get("mode"))
|
app_mode = AppMode.value_of(app_data.get("mode"))
|
||||||
|
@ -101,6 +102,7 @@ class AppDslService:
|
||||||
icon_type=icon_type,
|
icon_type=icon_type,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
icon_background=icon_background,
|
icon_background=icon_background,
|
||||||
|
use_icon_as_answer_icon=use_icon_as_answer_icon,
|
||||||
)
|
)
|
||||||
elif app_mode in [AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.COMPLETION]:
|
elif app_mode in [AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.COMPLETION]:
|
||||||
app = cls._import_and_create_new_model_config_based_app(
|
app = cls._import_and_create_new_model_config_based_app(
|
||||||
|
@ -113,6 +115,7 @@ class AppDslService:
|
||||||
icon_type=icon_type,
|
icon_type=icon_type,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
icon_background=icon_background,
|
icon_background=icon_background,
|
||||||
|
use_icon_as_answer_icon=use_icon_as_answer_icon,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid app mode")
|
raise ValueError("Invalid app mode")
|
||||||
|
@ -171,6 +174,7 @@ class AppDslService:
|
||||||
"icon": "🤖" if app_model.icon_type == "image" else app_model.icon,
|
"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,
|
"icon_background": "#FFEAD5" if app_model.icon_type == "image" else app_model.icon_background,
|
||||||
"description": app_model.description,
|
"description": app_model.description,
|
||||||
|
"use_icon_as_answer_icon": app_model.use_icon_as_answer_icon,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +222,7 @@ class AppDslService:
|
||||||
icon_type: str,
|
icon_type: str,
|
||||||
icon: str,
|
icon: str,
|
||||||
icon_background: str,
|
icon_background: str,
|
||||||
|
use_icon_as_answer_icon: bool,
|
||||||
) -> App:
|
) -> App:
|
||||||
"""
|
"""
|
||||||
Import app dsl and create new workflow based app
|
Import app dsl and create new workflow based app
|
||||||
|
@ -231,6 +236,7 @@ class AppDslService:
|
||||||
:param icon_type: app icon type, "emoji" or "image"
|
:param icon_type: app icon type, "emoji" or "image"
|
||||||
:param icon: app icon
|
:param icon: app icon
|
||||||
:param icon_background: app icon background
|
:param icon_background: app icon background
|
||||||
|
:param use_icon_as_answer_icon: use app icon as answer icon
|
||||||
"""
|
"""
|
||||||
if not workflow_data:
|
if not workflow_data:
|
||||||
raise ValueError("Missing workflow in data argument " "when app mode is advanced-chat or workflow")
|
raise ValueError("Missing workflow in data argument " "when app mode is advanced-chat or workflow")
|
||||||
|
@ -244,6 +250,7 @@ class AppDslService:
|
||||||
icon_type=icon_type,
|
icon_type=icon_type,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
icon_background=icon_background,
|
icon_background=icon_background,
|
||||||
|
use_icon_as_answer_icon=use_icon_as_answer_icon,
|
||||||
)
|
)
|
||||||
|
|
||||||
# init draft workflow
|
# init draft workflow
|
||||||
|
@ -316,6 +323,7 @@ class AppDslService:
|
||||||
icon_type: str,
|
icon_type: str,
|
||||||
icon: str,
|
icon: str,
|
||||||
icon_background: str,
|
icon_background: str,
|
||||||
|
use_icon_as_answer_icon: bool,
|
||||||
) -> App:
|
) -> App:
|
||||||
"""
|
"""
|
||||||
Import app dsl and create new model config based app
|
Import app dsl and create new model config based app
|
||||||
|
@ -341,6 +349,7 @@ class AppDslService:
|
||||||
icon_type=icon_type,
|
icon_type=icon_type,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
icon_background=icon_background,
|
icon_background=icon_background,
|
||||||
|
use_icon_as_answer_icon=use_icon_as_answer_icon,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_model_config = AppModelConfig()
|
app_model_config = AppModelConfig()
|
||||||
|
@ -369,6 +378,7 @@ class AppDslService:
|
||||||
icon_type: str,
|
icon_type: str,
|
||||||
icon: str,
|
icon: str,
|
||||||
icon_background: str,
|
icon_background: str,
|
||||||
|
use_icon_as_answer_icon: bool,
|
||||||
) -> App:
|
) -> App:
|
||||||
"""
|
"""
|
||||||
Create new app
|
Create new app
|
||||||
|
@ -381,6 +391,7 @@ class AppDslService:
|
||||||
:param icon_type: app icon type, "emoji" or "image"
|
:param icon_type: app icon type, "emoji" or "image"
|
||||||
:param icon: app icon
|
:param icon: app icon
|
||||||
:param icon_background: app icon background
|
:param icon_background: app icon background
|
||||||
|
:param use_icon_as_answer_icon: use app icon as answer icon
|
||||||
"""
|
"""
|
||||||
app = App(
|
app = App(
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
|
@ -392,6 +403,7 @@ class AppDslService:
|
||||||
icon_background=icon_background,
|
icon_background=icon_background,
|
||||||
enable_site=True,
|
enable_site=True,
|
||||||
enable_api=True,
|
enable_api=True,
|
||||||
|
use_icon_as_answer_icon=use_icon_as_answer_icon,
|
||||||
created_by=account.id,
|
created_by=account.id,
|
||||||
updated_by=account.id,
|
updated_by=account.id,
|
||||||
)
|
)
|
||||||
|
|
|
@ -221,6 +221,7 @@ class AppService:
|
||||||
app.icon_type = args.get("icon_type", "emoji")
|
app.icon_type = args.get("icon_type", "emoji")
|
||||||
app.icon = args.get("icon")
|
app.icon = args.get("icon")
|
||||||
app.icon_background = args.get("icon_background")
|
app.icon_background = args.get("icon_background")
|
||||||
|
app.use_icon_as_answer_icon = args.get("use_icon_as_answer_icon", False)
|
||||||
app.updated_by = current_user.id
|
app.updated_by = current_user.id
|
||||||
app.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
app.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
|
@ -79,6 +79,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||||
icon,
|
icon,
|
||||||
icon_background,
|
icon_background,
|
||||||
description,
|
description,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
await updateAppInfo({
|
await updateAppInfo({
|
||||||
|
@ -88,6 +89,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||||
icon,
|
icon,
|
||||||
icon_background,
|
icon_background,
|
||||||
description,
|
description,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
})
|
})
|
||||||
setShowEditModal(false)
|
setShowEditModal(false)
|
||||||
notify({
|
notify({
|
||||||
|
@ -370,6 +372,8 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
||||||
appIconBackground={app.icon_background}
|
appIconBackground={app.icon_background}
|
||||||
appIconUrl={app.icon_url}
|
appIconUrl={app.icon_url}
|
||||||
appDescription={app.description}
|
appDescription={app.description}
|
||||||
|
appMode={app.mode}
|
||||||
|
appUseIconAsAnswerIcon={app.use_icon_as_answer_icon}
|
||||||
show={showEditModal}
|
show={showEditModal}
|
||||||
onConfirm={onEdit}
|
onConfirm={onEdit}
|
||||||
onHide={() => setShowEditModal(false)}
|
onHide={() => setShowEditModal(false)}
|
||||||
|
|
|
@ -63,6 +63,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||||
icon,
|
icon,
|
||||||
icon_background,
|
icon_background,
|
||||||
description,
|
description,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
}) => {
|
}) => {
|
||||||
if (!appDetail)
|
if (!appDetail)
|
||||||
return
|
return
|
||||||
|
@ -74,6 +75,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||||
icon,
|
icon,
|
||||||
icon_background,
|
icon_background,
|
||||||
description,
|
description,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
})
|
})
|
||||||
setShowEditModal(false)
|
setShowEditModal(false)
|
||||||
notify({
|
notify({
|
||||||
|
@ -423,6 +425,8 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||||
appIconBackground={appDetail.icon_background}
|
appIconBackground={appDetail.icon_background}
|
||||||
appIconUrl={appDetail.icon_url}
|
appIconUrl={appDetail.icon_url}
|
||||||
appDescription={appDetail.description}
|
appDescription={appDetail.description}
|
||||||
|
appMode={appDetail.mode}
|
||||||
|
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
|
||||||
show={showEditModal}
|
show={showEditModal}
|
||||||
onConfirm={onEdit}
|
onConfirm={onEdit}
|
||||||
onHide={() => setShowEditModal(false)}
|
onHide={() => setShowEditModal(false)}
|
||||||
|
|
|
@ -43,6 +43,7 @@ export type ConfigParams = {
|
||||||
icon: string
|
icon: string
|
||||||
icon_background?: string
|
icon_background?: string
|
||||||
show_workflow_steps: boolean
|
show_workflow_steps: boolean
|
||||||
|
use_icon_as_answer_icon: boolean
|
||||||
enable_sso?: boolean
|
enable_sso?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
custom_disclaimer,
|
custom_disclaimer,
|
||||||
default_language,
|
default_language,
|
||||||
show_workflow_steps,
|
show_workflow_steps,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
} = appInfo.site
|
} = appInfo.site
|
||||||
const [inputInfo, setInputInfo] = useState({
|
const [inputInfo, setInputInfo] = useState({
|
||||||
title,
|
title,
|
||||||
|
@ -82,6 +84,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
privacyPolicy: privacy_policy,
|
privacyPolicy: privacy_policy,
|
||||||
customDisclaimer: custom_disclaimer,
|
customDisclaimer: custom_disclaimer,
|
||||||
show_workflow_steps,
|
show_workflow_steps,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
enable_sso: appInfo.enable_sso,
|
enable_sso: appInfo.enable_sso,
|
||||||
})
|
})
|
||||||
const [language, setLanguage] = useState(default_language)
|
const [language, setLanguage] = useState(default_language)
|
||||||
|
@ -94,6 +97,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
? { type: 'image', url: icon_url!, fileId: icon }
|
? { type: 'image', url: icon_url!, fileId: icon }
|
||||||
: { type: 'emoji', icon, background: icon_background! },
|
: { type: 'emoji', icon, background: icon_background! },
|
||||||
)
|
)
|
||||||
|
const isChatBot = appInfo.mode === 'chat' || appInfo.mode === 'advanced-chat' || appInfo.mode === 'agent-chat'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInputInfo({
|
setInputInfo({
|
||||||
|
@ -105,6 +109,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
privacyPolicy: privacy_policy,
|
privacyPolicy: privacy_policy,
|
||||||
customDisclaimer: custom_disclaimer,
|
customDisclaimer: custom_disclaimer,
|
||||||
show_workflow_steps,
|
show_workflow_steps,
|
||||||
|
use_icon_as_answer_icon,
|
||||||
enable_sso: appInfo.enable_sso,
|
enable_sso: appInfo.enable_sso,
|
||||||
})
|
})
|
||||||
setLanguage(default_language)
|
setLanguage(default_language)
|
||||||
|
@ -157,6 +162,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId,
|
icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId,
|
||||||
icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined,
|
icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined,
|
||||||
show_workflow_steps: inputInfo.show_workflow_steps,
|
show_workflow_steps: inputInfo.show_workflow_steps,
|
||||||
|
use_icon_as_answer_icon: inputInfo.use_icon_as_answer_icon,
|
||||||
enable_sso: inputInfo.enable_sso,
|
enable_sso: inputInfo.enable_sso,
|
||||||
}
|
}
|
||||||
await onSave?.(params)
|
await onSave?.(params)
|
||||||
|
@ -209,6 +215,18 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||||
onChange={onChange('desc')}
|
onChange={onChange('desc')}
|
||||||
placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
||||||
/>
|
/>
|
||||||
|
{isChatBot && (
|
||||||
|
<div className='w-full mt-4'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
|
<div className={`font-medium ${s.settingTitle} text-gray-900 `}>{t('app.answerIcon.title')}</div>
|
||||||
|
<Switch
|
||||||
|
defaultValue={inputInfo.use_icon_as_answer_icon}
|
||||||
|
onChange={v => setInputInfo({ ...inputInfo, use_icon_as_answer_icon: v })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className='body-xs-regular text-gray-500'>{t('app.answerIcon.description')}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
|
<div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
|
||||||
<SimpleSelect
|
<SimpleSelect
|
||||||
items={languages.filter(item => item.supported)}
|
items={languages.filter(item => item.supported)}
|
||||||
|
|
47
web/app/components/base/answer-icon/index.tsx
Normal file
47
web/app/components/base/answer-icon/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import { init } from 'emoji-mart'
|
||||||
|
import data from '@emoji-mart/data'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
import type { AppIconType } from '@/types/app'
|
||||||
|
|
||||||
|
init({ data })
|
||||||
|
|
||||||
|
export type AnswerIconProps = {
|
||||||
|
iconType?: AppIconType | null
|
||||||
|
icon?: string | null
|
||||||
|
background?: string | null
|
||||||
|
imageUrl?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnswerIcon: FC<AnswerIconProps> = ({
|
||||||
|
iconType,
|
||||||
|
icon,
|
||||||
|
background,
|
||||||
|
imageUrl,
|
||||||
|
}) => {
|
||||||
|
const wrapperClassName = classNames(
|
||||||
|
'flex',
|
||||||
|
'items-center',
|
||||||
|
'justify-center',
|
||||||
|
'w-full',
|
||||||
|
'h-full',
|
||||||
|
'rounded-full',
|
||||||
|
'border-[0.5px]',
|
||||||
|
'border-black/5',
|
||||||
|
'text-xl',
|
||||||
|
)
|
||||||
|
const isValidImageIcon = iconType === 'image' && imageUrl
|
||||||
|
return <div
|
||||||
|
className={wrapperClassName}
|
||||||
|
style={{ background: background || '#D5F5F6' }}
|
||||||
|
>
|
||||||
|
{isValidImageIcon
|
||||||
|
? <img src={imageUrl} className="w-full h-full rounded-full" alt="answer icon" />
|
||||||
|
: (icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnswerIcon
|
|
@ -13,6 +13,7 @@ import {
|
||||||
getUrl,
|
getUrl,
|
||||||
stopChatMessageResponding,
|
stopChatMessageResponding,
|
||||||
} from '@/service/share'
|
} from '@/service/share'
|
||||||
|
import AnswerIcon from '@/app/components/base/answer-icon'
|
||||||
|
|
||||||
const ChatWrapper = () => {
|
const ChatWrapper = () => {
|
||||||
const {
|
const {
|
||||||
|
@ -128,6 +129,15 @@ const ChatWrapper = () => {
|
||||||
isMobile,
|
isMobile,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
|
||||||
|
? <AnswerIcon
|
||||||
|
iconType={appData.site.icon_type}
|
||||||
|
icon={appData.site.icon}
|
||||||
|
background={appData.site.icon_background}
|
||||||
|
imageUrl={appData.site.icon_url}
|
||||||
|
/>
|
||||||
|
: null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chat
|
<Chat
|
||||||
appData={appData}
|
appData={appData}
|
||||||
|
@ -143,6 +153,7 @@ const ChatWrapper = () => {
|
||||||
allToolIcons={appMeta?.tool_icons || {}}
|
allToolIcons={appMeta?.tool_icons || {}}
|
||||||
onFeedback={handleFeedback}
|
onFeedback={handleFeedback}
|
||||||
suggestedQuestions={suggestedQuestions}
|
suggestedQuestions={suggestedQuestions}
|
||||||
|
answerIcon={answerIcon}
|
||||||
hideProcessDetail
|
hideProcessDetail
|
||||||
themeBuilder={themeBuilder}
|
themeBuilder={themeBuilder}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -65,6 +65,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
||||||
prompt_public: false,
|
prompt_public: false,
|
||||||
copyright: '',
|
copyright: '',
|
||||||
show_workflow_steps: true,
|
show_workflow_steps: true,
|
||||||
|
use_icon_as_answer_icon: app.use_icon_as_answer_icon,
|
||||||
},
|
},
|
||||||
plan: 'basic',
|
plan: 'basic',
|
||||||
} as AppData
|
} as AppData
|
||||||
|
|
|
@ -22,6 +22,7 @@ import Citation from '@/app/components/base/chat/chat/citation'
|
||||||
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
||||||
import type { Emoji } from '@/app/components/tools/types'
|
import type { Emoji } from '@/app/components/tools/types'
|
||||||
import type { AppData } from '@/models/share'
|
import type { AppData } from '@/models/share'
|
||||||
|
import AnswerIcon from '@/app/components/base/answer-icon'
|
||||||
|
|
||||||
type AnswerProps = {
|
type AnswerProps = {
|
||||||
item: ChatItem
|
item: ChatItem
|
||||||
|
@ -89,11 +90,7 @@ const Answer: FC<AnswerProps> = ({
|
||||||
<div className='flex mb-2 last:mb-0'>
|
<div className='flex mb-2 last:mb-0'>
|
||||||
<div className='shrink-0 relative w-10 h-10'>
|
<div className='shrink-0 relative w-10 h-10'>
|
||||||
{
|
{
|
||||||
answerIcon || (
|
answerIcon || <AnswerIcon />
|
||||||
<div className='flex items-center justify-center w-full h-full rounded-full bg-[#d5f5f6] border-[0.5px] border-black/5 text-xl'>
|
|
||||||
🤖
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
responding && (
|
responding && (
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
stopChatMessageResponding,
|
stopChatMessageResponding,
|
||||||
} from '@/service/share'
|
} from '@/service/share'
|
||||||
import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar'
|
import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar'
|
||||||
|
import AnswerIcon from '@/app/components/base/answer-icon'
|
||||||
|
|
||||||
const ChatWrapper = () => {
|
const ChatWrapper = () => {
|
||||||
const {
|
const {
|
||||||
|
@ -114,6 +115,17 @@ const ChatWrapper = () => {
|
||||||
return null
|
return null
|
||||||
}, [currentConversationId, inputsForms, isMobile])
|
}, [currentConversationId, inputsForms, isMobile])
|
||||||
|
|
||||||
|
const answerIcon = isDify()
|
||||||
|
? <LogoAvatar className='relative shrink-0' />
|
||||||
|
: (appData?.site && appData.site.use_icon_as_answer_icon)
|
||||||
|
? <AnswerIcon
|
||||||
|
iconType={appData.site.icon_type}
|
||||||
|
icon={appData.site.icon}
|
||||||
|
background={appData.site.icon_background}
|
||||||
|
imageUrl={appData.site.icon_url}
|
||||||
|
/>
|
||||||
|
: null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chat
|
<Chat
|
||||||
appData={appData}
|
appData={appData}
|
||||||
|
@ -129,7 +141,7 @@ const ChatWrapper = () => {
|
||||||
allToolIcons={appMeta?.tool_icons || {}}
|
allToolIcons={appMeta?.tool_icons || {}}
|
||||||
onFeedback={handleFeedback}
|
onFeedback={handleFeedback}
|
||||||
suggestedQuestions={suggestedQuestions}
|
suggestedQuestions={suggestedQuestions}
|
||||||
answerIcon={isDify() ? <LogoAvatar className='relative shrink-0' /> : null}
|
answerIcon={answerIcon}
|
||||||
hideProcessDetail
|
hideProcessDetail
|
||||||
themeBuilder={themeBuilder}
|
themeBuilder={themeBuilder}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { RiCloseLine } from '@remixicon/react'
|
||||||
import AppIconPicker from '../../base/app-icon-picker'
|
import AppIconPicker from '../../base/app-icon-picker'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
|
import Switch from '@/app/components/base/switch'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
@ -20,12 +21,15 @@ export type CreateAppModalProps = {
|
||||||
appIcon: string
|
appIcon: string
|
||||||
appIconBackground?: string | null
|
appIconBackground?: string | null
|
||||||
appIconUrl?: string | null
|
appIconUrl?: string | null
|
||||||
|
appMode?: string
|
||||||
|
appUseIconAsAnswerIcon?: boolean
|
||||||
onConfirm: (info: {
|
onConfirm: (info: {
|
||||||
name: string
|
name: string
|
||||||
icon_type: AppIconType
|
icon_type: AppIconType
|
||||||
icon: string
|
icon: string
|
||||||
icon_background?: string
|
icon_background?: string
|
||||||
description: string
|
description: string
|
||||||
|
use_icon_as_answer_icon?: boolean
|
||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
onHide: () => void
|
onHide: () => void
|
||||||
}
|
}
|
||||||
|
@ -39,6 +43,8 @@ const CreateAppModal = ({
|
||||||
appIconUrl,
|
appIconUrl,
|
||||||
appName,
|
appName,
|
||||||
appDescription,
|
appDescription,
|
||||||
|
appMode,
|
||||||
|
appUseIconAsAnswerIcon,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onHide,
|
onHide,
|
||||||
}: CreateAppModalProps) => {
|
}: CreateAppModalProps) => {
|
||||||
|
@ -52,6 +58,7 @@ const CreateAppModal = ({
|
||||||
)
|
)
|
||||||
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
|
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
|
||||||
const [description, setDescription] = useState(appDescription || '')
|
const [description, setDescription] = useState(appDescription || '')
|
||||||
|
const [useIconAsAnswerIcon, setUseIconAsAnswerIcon] = useState(appUseIconAsAnswerIcon || false)
|
||||||
|
|
||||||
const { plan, enableBilling } = useProviderContext()
|
const { plan, enableBilling } = useProviderContext()
|
||||||
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
|
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
|
||||||
|
@ -67,6 +74,7 @@ const CreateAppModal = ({
|
||||||
icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId,
|
icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId,
|
||||||
icon_background: appIcon.type === 'emoji' ? appIcon.background! : undefined,
|
icon_background: appIcon.type === 'emoji' ? appIcon.background! : undefined,
|
||||||
description,
|
description,
|
||||||
|
use_icon_as_answer_icon: useIconAsAnswerIcon,
|
||||||
})
|
})
|
||||||
onHide()
|
onHide()
|
||||||
}
|
}
|
||||||
|
@ -119,6 +127,19 @@ const CreateAppModal = ({
|
||||||
onChange={e => setDescription(e.target.value)}
|
onChange={e => setDescription(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/* answer icon */}
|
||||||
|
{isEditModal && (appMode === 'chat' || appMode === 'advanced-chat' || appMode === 'agent-chat') && (
|
||||||
|
<div className='pt-2'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
|
<div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.answerIcon.title')}</div>
|
||||||
|
<Switch
|
||||||
|
defaultValue={useIconAsAnswerIcon}
|
||||||
|
onChange={v => setUseIconAsAnswerIcon(v)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className='body-xs-regular text-gray-500'>{t('app.answerIcon.descriptionInExplore')}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />}
|
{!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-row-reverse'>
|
<div className='flex flex-row-reverse'>
|
||||||
|
|
|
@ -77,6 +77,11 @@ const translation = {
|
||||||
emoji: 'Emoji',
|
emoji: 'Emoji',
|
||||||
image: 'Image',
|
image: 'Image',
|
||||||
},
|
},
|
||||||
|
answerIcon: {
|
||||||
|
title: 'Use WebApp icon to replace 🤖',
|
||||||
|
description: 'Wether to use the WebApp icon to replace 🤖 in the shared application',
|
||||||
|
descriptionInExplore: 'Whether to use the WebApp icon to replace 🤖 in Explore',
|
||||||
|
},
|
||||||
switch: 'Switch to Workflow Orchestrate',
|
switch: 'Switch to Workflow Orchestrate',
|
||||||
switchTipStart: 'A new app copy will be created for you, and the new copy will switch to Workflow Orchestrate. The new copy will ',
|
switchTipStart: 'A new app copy will be created for you, and the new copy will switch to Workflow Orchestrate. The new copy will ',
|
||||||
switchTip: 'not allow',
|
switchTip: 'not allow',
|
||||||
|
|
|
@ -76,6 +76,11 @@ const translation = {
|
||||||
emoji: '表情符号',
|
emoji: '表情符号',
|
||||||
image: '图片',
|
image: '图片',
|
||||||
},
|
},
|
||||||
|
answerIcon: {
|
||||||
|
title: '使用 WebApp 图标替换 🤖',
|
||||||
|
description: '是否使用 WebApp 图标替换分享的应用界面中的 🤖',
|
||||||
|
descriptionInExplore: '是否使用 WebApp 图标替换 Explore 界面中的 🤖',
|
||||||
|
},
|
||||||
switch: '迁移为工作流编排',
|
switch: '迁移为工作流编排',
|
||||||
switchTipStart: '将为您创建一个使用工作流编排的新应用。新应用将',
|
switchTipStart: '将为您创建一个使用工作流编排的新应用。新应用将',
|
||||||
switchTip: '不能够',
|
switchTip: '不能够',
|
||||||
|
|
|
@ -8,6 +8,7 @@ export type AppBasicInfo = {
|
||||||
icon_url: string
|
icon_url: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
|
use_icon_as_answer_icon: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant'
|
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant'
|
||||||
|
|
|
@ -25,6 +25,7 @@ export type SiteInfo = {
|
||||||
privacy_policy?: string
|
privacy_policy?: string
|
||||||
custom_disclaimer?: string
|
custom_disclaimer?: string
|
||||||
show_workflow_steps?: boolean
|
show_workflow_steps?: boolean
|
||||||
|
use_icon_as_answer_icon?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppMeta = {
|
export type AppMeta = {
|
||||||
|
|
|
@ -28,8 +28,8 @@ export const createApp: Fetcher<AppDetailResponse, { name: string; icon_type?: A
|
||||||
return post<AppDetailResponse>('apps', { body: { name, icon_type, icon, icon_background, mode, description, model_config: config } })
|
return post<AppDetailResponse>('apps', { body: { name, icon_type, icon, icon_background, mode, description, model_config: config } })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateAppInfo: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string; description: string }> = ({ appID, name, icon_type, icon, icon_background, description }) => {
|
export const updateAppInfo: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string; description: string; use_icon_as_answer_icon?: boolean }> = ({ appID, name, icon_type, icon, icon_background, description, use_icon_as_answer_icon }) => {
|
||||||
return put<AppDetailResponse>(`apps/${appID}`, { body: { name, icon_type, icon, icon_background, description } })
|
return put<AppDetailResponse>(`apps/${appID}`, { body: { name, icon_type, icon, icon_background, description, use_icon_as_answer_icon } })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const copyApp: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string | null; mode: AppMode; description?: string }> = ({ appID, name, icon_type, icon, icon_background, mode, description }) => {
|
export const copyApp: Fetcher<AppDetailResponse, { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string | null; mode: AppMode; description?: string }> = ({ appID, name, icon_type, icon, icon_background, mode, description }) => {
|
||||||
|
|
|
@ -297,6 +297,7 @@ export type SiteConfig = {
|
||||||
icon_url: string | null
|
icon_url: string | null
|
||||||
|
|
||||||
show_workflow_steps: boolean
|
show_workflow_steps: boolean
|
||||||
|
use_icon_as_answer_icon: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppIconType = 'image' | 'emoji'
|
export type AppIconType = 'image' | 'emoji'
|
||||||
|
@ -323,6 +324,8 @@ export type App = {
|
||||||
icon_background: string | null
|
icon_background: string | null
|
||||||
/** Icon URL, only available when icon_type is 'image' */
|
/** Icon URL, only available when icon_type is 'image' */
|
||||||
icon_url: string | null
|
icon_url: string | null
|
||||||
|
/** Whether to use app icon as answer icon */
|
||||||
|
use_icon_as_answer_icon: boolean
|
||||||
|
|
||||||
/** Mode */
|
/** Mode */
|
||||||
mode: AppMode
|
mode: AppMode
|
||||||
|
|
Loading…
Reference in New Issue
Block a user