| @@ -28,6 +28,7 @@ def parse_app_site_args(): | |||
| 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') | |||
| return parser.parse_args() | |||
| @@ -59,7 +60,8 @@ class AppSite(Resource): | |||
| 'privacy_policy', | |||
| 'custom_disclaimer', | |||
| 'customize_token_strategy', | |||
| 'prompt_public' | |||
| 'prompt_public', | |||
| 'show_workflow_steps' | |||
| ]: | |||
| value = args.get(attr_name) | |||
| if value is not None: | |||
| @@ -33,7 +33,8 @@ class AppSiteApi(WebApiResource): | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'default_language': fields.String, | |||
| 'prompt_public': fields.Boolean | |||
| 'prompt_public': fields.Boolean, | |||
| 'show_workflow_steps': fields.Boolean, | |||
| } | |||
| app_fields = { | |||
| @@ -117,6 +117,7 @@ site_fields = { | |||
| 'customize_token_strategy': fields.String, | |||
| 'prompt_public': fields.Boolean, | |||
| 'app_base_url': fields.String, | |||
| 'show_workflow_steps': fields.Boolean, | |||
| } | |||
| app_detail_fields_with_site = { | |||
| @@ -149,5 +150,6 @@ app_site_fields = { | |||
| 'privacy_policy': fields.String, | |||
| 'custom_disclaimer': fields.String, | |||
| 'customize_token_strategy': fields.String, | |||
| 'prompt_public': fields.Boolean | |||
| 'prompt_public': fields.Boolean, | |||
| 'show_workflow_steps': fields.Boolean, | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| """add workflow to site | |||
| Revision ID: 4ff534e1eb11 | |||
| Revises: 7b45942e39bb | |||
| Create Date: 2024-06-21 04:16:03.419634 | |||
| """ | |||
| import sqlalchemy as sa | |||
| from alembic import op | |||
| import models as models | |||
| # revision identifiers, used by Alembic. | |||
| revision = '4ff534e1eb11' | |||
| down_revision = '7b45942e39bb' | |||
| branch_labels = None | |||
| depends_on = None | |||
| def upgrade(): | |||
| # ### commands auto generated by Alembic - please adjust! ### | |||
| with op.batch_alter_table('sites', schema=None) as batch_op: | |||
| batch_op.add_column(sa.Column('show_workflow_steps', sa.Boolean(), server_default=sa.text('true'), 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('show_workflow_steps') | |||
| # ### end Alembic commands ### | |||
| @@ -1043,6 +1043,7 @@ class Site(db.Model): | |||
| default_language = db.Column(db.String(255), nullable=False) | |||
| copyright = 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')) | |||
| custom_disclaimer = db.Column(db.String(255), nullable=True) | |||
| customize_domain = db.Column(db.String(255)) | |||
| customize_token_strategy = db.Column(db.String(255), nullable=False) | |||
| @@ -34,6 +34,7 @@ export type ConfigParams = { | |||
| custom_disclaimer: string | |||
| icon: string | |||
| icon_background: string | |||
| show_workflow_steps: boolean | |||
| } | |||
| const prefixSettings = 'appOverview.overview.appInfo.settings' | |||
| @@ -47,8 +48,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| const { notify } = useToastContext() | |||
| const [isShowMore, setIsShowMore] = useState(false) | |||
| const { icon, icon_background } = appInfo | |||
| const { title, description, copyright, privacy_policy, custom_disclaimer, default_language } = appInfo.site | |||
| const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer }) | |||
| const { title, description, copyright, privacy_policy, custom_disclaimer, default_language, show_workflow_steps } = appInfo.site | |||
| const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps }) | |||
| const [language, setLanguage] = useState(default_language) | |||
| const [saveLoading, setSaveLoading] = useState(false) | |||
| const { t } = useTranslation() | |||
| @@ -57,7 +58,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| const [emoji, setEmoji] = useState({ icon, icon_background }) | |||
| useEffect(() => { | |||
| setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer }) | |||
| setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps }) | |||
| setLanguage(default_language) | |||
| setEmoji({ icon, icon_background }) | |||
| }, [appInfo]) | |||
| @@ -85,6 +86,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| custom_disclaimer: inputInfo.customDisclaimer, | |||
| icon: emoji.icon, | |||
| icon_background: emoji.icon_background, | |||
| show_workflow_steps: inputInfo.show_workflow_steps, | |||
| } | |||
| await onSave?.(params) | |||
| setSaveLoading(false) | |||
| @@ -134,6 +136,14 @@ const SettingsModal: FC<ISettingsModalProps> = ({ | |||
| defaultValue={language} | |||
| onSelect={item => setLanguage(item.value as Language)} | |||
| /> | |||
| {(appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat') && <> | |||
| <div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.workflow.title`)}</div> | |||
| <SimpleSelect | |||
| items={[{ name: t(`${prefixSettings}.workflow.show`), value: 'true' }, { name: t(`${prefixSettings}.workflow.hide`), value: 'false' }]} | |||
| defaultValue={inputInfo.show_workflow_steps ? 'true' : 'false'} | |||
| onSelect={item => setInputInfo({ ...inputInfo, show_workflow_steps: item.value === 'true' })} | |||
| /> | |||
| </>} | |||
| {!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}> | |||
| <div className='flex justify-between'> | |||
| <div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div> | |||
| @@ -28,6 +28,7 @@ import EditReplyModal from '@/app/components/app/annotation/edit-annotation-moda | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process' | |||
| import type { WorkflowProcess } from '@/app/components/base/chat/types' | |||
| import type { SiteInfo } from '@/models/share' | |||
| const MAX_DEPTH = 3 | |||
| @@ -62,6 +63,7 @@ export type IGenerationItemProps = { | |||
| contentClassName?: string | |||
| footerClassName?: string | |||
| hideProcessDetail?: boolean | |||
| siteInfo: SiteInfo | null | |||
| } | |||
| export const SimpleBtn = ({ className, isDisabled, onClick, children }: { | |||
| @@ -113,6 +115,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({ | |||
| innerClassName, | |||
| contentClassName, | |||
| hideProcessDetail, | |||
| siteInfo, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const params = useParams() | |||
| @@ -152,6 +155,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({ | |||
| installedAppId, | |||
| controlClearMoreLikeThis, | |||
| isWorkflow, | |||
| siteInfo, | |||
| } | |||
| const handleMoreLikeThis = async () => { | |||
| @@ -297,7 +301,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({ | |||
| } | |||
| <div className={`flex ${contentClassName}`}> | |||
| <div className='grow w-0'> | |||
| {workflowProcessData && ( | |||
| {siteInfo && siteInfo.show_workflow_steps && workflowProcessData && ( | |||
| <WorkflowProcessItem grayBg hideInfo data={workflowProcessData} expand={workflowProcessData.expand} hideProcessDetail={hideProcessDetail} /> | |||
| )} | |||
| {workflowProcessData && !isError && ( | |||
| @@ -29,6 +29,7 @@ const ChatWrapper = () => { | |||
| appMeta, | |||
| handleFeedback, | |||
| currentChatInstanceRef, | |||
| appData, | |||
| } = useChatWithHistoryContext() | |||
| const appConfig = useMemo(() => { | |||
| const config = appParams || {} | |||
| @@ -128,6 +129,7 @@ const ChatWrapper = () => { | |||
| return ( | |||
| <Chat | |||
| appData={appData} | |||
| config={appConfig} | |||
| chatList={chatList} | |||
| isResponding={isResponding} | |||
| @@ -47,7 +47,14 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| const { id, app } = installedAppInfo! | |||
| return { | |||
| app_id: id, | |||
| site: { title: app.name, icon: app.icon, icon_background: app.icon_background, prompt_public: false, copyright: '' }, | |||
| site: { | |||
| title: app.name, | |||
| icon: app.icon, | |||
| icon_background: app.icon_background, | |||
| prompt_public: false, | |||
| copyright: '', | |||
| show_workflow_steps: true, | |||
| }, | |||
| plan: 'basic', | |||
| } as AppData | |||
| } | |||
| @@ -20,6 +20,7 @@ import LoadingAnim from '@/app/components/app/chat/loading-anim' | |||
| import Citation from '@/app/components/app/chat/citation' | |||
| import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item' | |||
| import type { Emoji } from '@/app/components/tools/types' | |||
| import type { AppData } from '@/models/share' | |||
| type AnswerProps = { | |||
| item: ChatItem | |||
| @@ -32,6 +33,7 @@ type AnswerProps = { | |||
| showPromptLog?: boolean | |||
| chatAnswerContainerInner?: string | |||
| hideProcessDetail?: boolean | |||
| appData?: AppData | |||
| } | |||
| const Answer: FC<AnswerProps> = ({ | |||
| item, | |||
| @@ -44,6 +46,7 @@ const Answer: FC<AnswerProps> = ({ | |||
| showPromptLog, | |||
| chatAnswerContainerInner, | |||
| hideProcessDetail, | |||
| appData, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const { | |||
| @@ -129,8 +132,20 @@ const Answer: FC<AnswerProps> = ({ | |||
| /> | |||
| ) | |||
| } | |||
| {/** Render the normal steps */} | |||
| { | |||
| workflowProcess && ( | |||
| workflowProcess && !hideProcessDetail && ( | |||
| <WorkflowProcess | |||
| data={workflowProcess} | |||
| item={item} | |||
| hideInfo | |||
| hideProcessDetail={hideProcessDetail} | |||
| /> | |||
| ) | |||
| } | |||
| {/** Hide workflow steps by it's settings in siteInfo */} | |||
| { | |||
| workflowProcess && hideProcessDetail && appData && appData.site.show_workflow_steps && ( | |||
| <WorkflowProcess | |||
| data={workflowProcess} | |||
| item={item} | |||
| @@ -30,8 +30,10 @@ import { StopCircle } from '@/app/components/base/icons/src/vender/solid/mediaAn | |||
| import AgentLogModal from '@/app/components/base/agent-log-modal' | |||
| import PromptLogModal from '@/app/components/base/prompt-log-modal' | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| import type { AppData } from '@/models/share' | |||
| export type ChatProps = { | |||
| appData?: AppData | |||
| chatList: ChatItem[] | |||
| config?: ChatConfig | |||
| isResponding?: boolean | |||
| @@ -57,6 +59,7 @@ export type ChatProps = { | |||
| hideProcessDetail?: boolean | |||
| } | |||
| const Chat: FC<ChatProps> = ({ | |||
| appData, | |||
| config, | |||
| onSend, | |||
| chatList, | |||
| @@ -196,6 +199,7 @@ const Chat: FC<ChatProps> = ({ | |||
| const isLast = item.id === chatList[chatList.length - 1]?.id | |||
| return ( | |||
| <Answer | |||
| appData={appData} | |||
| key={item.id} | |||
| item={item} | |||
| question={chatList[index - 1]?.content} | |||
| @@ -18,6 +18,7 @@ import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar' | |||
| const ChatWrapper = () => { | |||
| const { | |||
| appData, | |||
| appParams, | |||
| appPrevChatList, | |||
| currentConversationId, | |||
| @@ -114,6 +115,7 @@ const ChatWrapper = () => { | |||
| return ( | |||
| <Chat | |||
| appData={appData} | |||
| config={appConfig} | |||
| chatList={chatList} | |||
| isResponding={isResponding} | |||
| @@ -442,6 +442,7 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| visionConfig={visionConfig} | |||
| completionFiles={completionFiles} | |||
| isShowTextToSpeech={!!textToSpeechConfig?.enabled} | |||
| siteInfo={siteInfo} | |||
| />) | |||
| const renderBatchRes = () => { | |||
| @@ -18,6 +18,7 @@ import { TransferMethod, type VisionFile, type VisionSettings } from '@/types/ap | |||
| import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types' | |||
| import type { WorkflowProcess } from '@/app/components/base/chat/types' | |||
| import { sleep } from '@/utils' | |||
| import type { SiteInfo } from '@/models/share' | |||
| export type IResultProps = { | |||
| isWorkflow: boolean | |||
| @@ -42,6 +43,7 @@ export type IResultProps = { | |||
| moderationService?: (text: string) => ReturnType<ModerationService> | |||
| visionConfig: VisionSettings | |||
| completionFiles: VisionFile[] | |||
| siteInfo: SiteInfo | null | |||
| } | |||
| const Result: FC<IResultProps> = ({ | |||
| @@ -65,6 +67,7 @@ const Result: FC<IResultProps> = ({ | |||
| onCompleted, | |||
| visionConfig, | |||
| completionFiles, | |||
| siteInfo, | |||
| }) => { | |||
| const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false) | |||
| useEffect(() => { | |||
| @@ -375,6 +378,7 @@ const Result: FC<IResultProps> = ({ | |||
| controlClearMoreLikeThis={controlClearMoreLikeThis} | |||
| isShowTextToSpeech={isShowTextToSpeech} | |||
| hideProcessDetail | |||
| siteInfo={siteInfo} | |||
| /> | |||
| ) | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Dieser Text wird auf der Clientseite angezeigt und bietet grundlegende Anleitungen zur Verwendung der Anwendung', | |||
| webDescPlaceholder: 'Geben Sie die Beschreibung der WebApp ein', | |||
| language: 'Sprache', | |||
| workflow: { | |||
| title: 'Workflow-Schritte', | |||
| show: 'Anzeigen', | |||
| hide: 'Verbergen', | |||
| }, | |||
| more: { | |||
| entry: 'Mehr Einstellungen anzeigen', | |||
| copyright: 'Urheberrecht', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'This text will be displayed on the client side, providing basic guidance on how to use the application', | |||
| webDescPlaceholder: 'Enter the description of the WebApp', | |||
| language: 'Language', | |||
| workflow: { | |||
| title: 'Workflow Steps', | |||
| show: 'Show', | |||
| hide: 'Hide', | |||
| }, | |||
| more: { | |||
| entry: 'Show more settings', | |||
| copyright: 'Copyright', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Ce texte sera affiché côté client, fournissant des directives de base sur la façon d\'utiliser l\'application', | |||
| webDescPlaceholder: 'Entrez la description de l\'application Web', | |||
| language: 'Langue', | |||
| workflow: { | |||
| title: 'Étapes du workflow', | |||
| show: 'Afficher', | |||
| hide: 'Masquer', | |||
| }, | |||
| more: { | |||
| entry: 'Afficher plus de paramètres', | |||
| copyright: 'Droits d\'auteur', | |||
| @@ -48,6 +48,11 @@ const translation = { | |||
| 'यह टेक्स्ट क्लाइंट साइड पर प्रदर्शित होगा, जो एप्लिकेशन का उपयोग करने के लिए बुनियादी मार्गदर्शन प्रदान करेगा', | |||
| webDescPlaceholder: 'वेबऐप का विवरण दर्ज करें', | |||
| language: 'भाषा', | |||
| workflow: { | |||
| title: 'वर्कफ़्लो स्टेप्स', | |||
| show: 'दिखाएं', | |||
| hide: 'छुपाएं', | |||
| }, | |||
| more: { | |||
| entry: 'अधिक सेटिंग्स दिखाएं', | |||
| copyright: 'कॉपीराइट', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'このテキストはクライアント側に表示され、アプリケーションの使用方法の基本的なガイダンスを提供します。', | |||
| webDescPlaceholder: 'WebAppの説明を入力してください', | |||
| language: '言語', | |||
| workflow: { | |||
| title: 'ワークフローステップ', | |||
| show: '表示', | |||
| hide: '非表示', | |||
| }, | |||
| more: { | |||
| entry: 'その他の設定を表示', | |||
| copyright: '著作権', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: '이 텍스트는 클라이언트 측에서 표시되며, 애플리케이션의 사용 방법에 대한 기본적인 안내를 제공합니다.', | |||
| webDescPlaceholder: '웹앱 설명을 입력하세요', | |||
| language: '언어', | |||
| workflow: { | |||
| title: '워크플로 단계', | |||
| show: '표시', | |||
| hide: '숨기기', | |||
| }, | |||
| more: { | |||
| entry: '추가 설정 보기', | |||
| copyright: '저작권', | |||
| @@ -48,6 +48,11 @@ const translation = { | |||
| 'Ten tekst będzie wyświetlany po stronie klienta, zapewniając podstawowe wskazówki, jak korzystać z aplikacji', | |||
| webDescPlaceholder: 'Wpisz opis WebApp', | |||
| language: 'Język', | |||
| workflow: { | |||
| title: 'Kroki przepływu pracy', | |||
| show: 'Pokaż', | |||
| hide: 'Ukryj', | |||
| }, | |||
| more: { | |||
| entry: 'Pokaż więcej ustawień', | |||
| copyright: 'Prawa autorskie', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Este texto será exibido no lado do cliente, fornecendo orientações básicas sobre como usar o aplicativo', | |||
| webDescPlaceholder: 'Insira a descrição do WebApp', | |||
| language: 'Idioma', | |||
| workflow: { | |||
| title: 'Etapas do fluxo de trabalho', | |||
| show: 'Mostrar', | |||
| hide: 'Ocultar', | |||
| }, | |||
| more: { | |||
| entry: 'Mostrar mais configurações', | |||
| copyright: 'Direitos autorais', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Acest text va fi afișat pe partea clientului, oferind îndrumare de bază privind modul de utilizare a aplicației', | |||
| webDescPlaceholder: 'Introduceți descrierea aplicației web', | |||
| language: 'Limbă', | |||
| workflow: { | |||
| title: 'Pași flux de lucru', | |||
| show: 'Afișați', | |||
| hide: 'Ascundeți', | |||
| }, | |||
| more: { | |||
| entry: 'Afișați mai multe setări', | |||
| copyright: 'Drepturi de autor', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Цей текст буде відображений на клієнтському боці, надаючи базові вказівки щодо використання додатка', | |||
| webDescPlaceholder: 'Введіть опис веб-додатку', | |||
| language: 'Мова', | |||
| workflow: { | |||
| title: 'Кроки робочого процесу', | |||
| show: 'Показати', | |||
| hide: 'Приховати', | |||
| }, | |||
| more: { | |||
| entry: 'Показати додаткові налаштування', | |||
| copyright: 'Авторське право', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: 'Văn bản này sẽ được hiển thị ở phía máy khách, cung cấp hướng dẫn cơ bản về cách sử dụng ứng dụng', | |||
| webDescPlaceholder: 'Nhập mô tả của ứng dụng web', | |||
| language: 'Ngôn ngữ', | |||
| workflow: { | |||
| title: 'Các Bước Quy trình', | |||
| show: 'Hiển thị', | |||
| hide: 'Ẩn', | |||
| }, | |||
| more: { | |||
| entry: 'Hiển thị thêm cài đặt', | |||
| copyright: 'Bản quyền', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: '以下文字将展示在客户端中,对应用进行说明和使用上的基本引导', | |||
| webDescPlaceholder: '请输入 WebApp 的描述', | |||
| language: '语言', | |||
| workflow: { | |||
| title: '工作流详情', | |||
| show: '显示', | |||
| hide: '隐藏', | |||
| }, | |||
| more: { | |||
| entry: '展示更多设置', | |||
| copyright: '版权', | |||
| @@ -44,6 +44,11 @@ const translation = { | |||
| webDescTip: '以下文字將展示在客戶端中,對應用進行說明和使用上的基本引導', | |||
| webDescPlaceholder: '請輸入 WebApp 的描述', | |||
| language: '語言', | |||
| workflow: { | |||
| title: '工作流程步驟', | |||
| show: '展示', | |||
| hide: '隱藏', | |||
| }, | |||
| more: { | |||
| entry: '展示更多設定', | |||
| copyright: '版權', | |||
| @@ -19,6 +19,7 @@ export type SiteInfo = { | |||
| copyright?: string | |||
| privacy_policy?: string | |||
| custom_disclaimer?: string | |||
| show_workflow_steps?: boolean | |||
| } | |||
| export type AppMeta = { | |||
| @@ -274,6 +274,8 @@ export type SiteConfig = { | |||
| icon: string | |||
| icon_background: string | |||
| show_workflow_steps: boolean | |||
| } | |||
| /** | |||