Co-authored-by: crazywoola <427733928@qq.com>tags/0.7.3
| 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() |
| ) | ) | ||||
| 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() | ||||
| "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: |
| "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 = { |
| "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, | ||||
| "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, | ||||
| "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, | ||||
| "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, | ||||
| "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, | |||||
| } | } |
| "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 = { |
| """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 ### |
| 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): | ||||
| 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) |
| 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")) | ||||
| 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( | ||||
| 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") | ||||
| "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, | |||||
| }, | }, | ||||
| } | } | ||||
| 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 | ||||
| :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") | ||||
| 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 | ||||
| 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 | ||||
| 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() | ||||
| 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 | ||||
| :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, | ||||
| 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, | ||||
| ) | ) |
| 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() |
| icon, | icon, | ||||
| icon_background, | icon_background, | ||||
| description, | description, | ||||
| use_icon_as_answer_icon, | |||||
| }) => { | }) => { | ||||
| try { | try { | ||||
| await updateAppInfo({ | await updateAppInfo({ | ||||
| icon, | icon, | ||||
| icon_background, | icon_background, | ||||
| description, | description, | ||||
| use_icon_as_answer_icon, | |||||
| }) | }) | ||||
| setShowEditModal(false) | setShowEditModal(false) | ||||
| notify({ | notify({ | ||||
| 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)} |
| icon, | icon, | ||||
| icon_background, | icon_background, | ||||
| description, | description, | ||||
| use_icon_as_answer_icon, | |||||
| }) => { | }) => { | ||||
| if (!appDetail) | if (!appDetail) | ||||
| return | return | ||||
| icon, | icon, | ||||
| icon_background, | icon_background, | ||||
| description, | description, | ||||
| use_icon_as_answer_icon, | |||||
| }) | }) | ||||
| setShowEditModal(false) | setShowEditModal(false) | ||||
| notify({ | notify({ | ||||
| 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)} |
| 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 | ||||
| } | } | ||||
| 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, | ||||
| 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) | ||||
| ? { 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({ | ||||
| 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) | ||||
| 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) | ||||
| 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)} |
| '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 |
| getUrl, | getUrl, | ||||
| stopChatMessageResponding, | stopChatMessageResponding, | ||||
| } from '@/service/share' | } from '@/service/share' | ||||
| import AnswerIcon from '@/app/components/base/answer-icon' | |||||
| const ChatWrapper = () => { | const ChatWrapper = () => { | ||||
| const { | const { | ||||
| 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} | ||||
| allToolIcons={appMeta?.tool_icons || {}} | allToolIcons={appMeta?.tool_icons || {}} | ||||
| onFeedback={handleFeedback} | onFeedback={handleFeedback} | ||||
| suggestedQuestions={suggestedQuestions} | suggestedQuestions={suggestedQuestions} | ||||
| answerIcon={answerIcon} | |||||
| hideProcessDetail | hideProcessDetail | ||||
| themeBuilder={themeBuilder} | themeBuilder={themeBuilder} | ||||
| /> | /> |
| 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 |
| 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 | ||||
| <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 || ( | |||||
| <div className='flex items-center justify-center w-full h-full rounded-full bg-[#d5f5f6] border-[0.5px] border-black/5 text-xl'> | |||||
| 🤖 | |||||
| </div> | |||||
| ) | |||||
| answerIcon || <AnswerIcon /> | |||||
| } | } | ||||
| { | { | ||||
| responding && ( | responding && ( |
| 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 { | ||||
| 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} | ||||
| 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} | ||||
| /> | /> |
| 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' | ||||
| 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 | ||||
| } | } | ||||
| appIconUrl, | appIconUrl, | ||||
| appName, | appName, | ||||
| appDescription, | appDescription, | ||||
| appMode, | |||||
| appUseIconAsAnswerIcon, | |||||
| onConfirm, | onConfirm, | ||||
| onHide, | onHide, | ||||
| }: CreateAppModalProps) => { | }: CreateAppModalProps) => { | ||||
| ) | ) | ||||
| 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) | ||||
| 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() | ||||
| } | } | ||||
| 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'> |
| 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', |
| emoji: '表情符号', | emoji: '表情符号', | ||||
| image: '图片', | image: '图片', | ||||
| }, | }, | ||||
| answerIcon: { | |||||
| title: '使用 WebApp 图标替换 🤖', | |||||
| description: '是否使用 WebApp 图标替换分享的应用界面中的 🤖', | |||||
| descriptionInExplore: '是否使用 WebApp 图标替换 Explore 界面中的 🤖', | |||||
| }, | |||||
| switch: '迁移为工作流编排', | switch: '迁移为工作流编排', | ||||
| switchTipStart: '将为您创建一个使用工作流编排的新应用。新应用将', | switchTipStart: '将为您创建一个使用工作流编排的新应用。新应用将', | ||||
| switchTip: '不能够', | switchTip: '不能够', |
| 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' |
| 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 = { |
| 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 }) => { | |||||
| return put<AppDetailResponse>(`apps/${appID}`, { body: { 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, 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 }) => { |
| 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' | ||||
| 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 |