diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b4a6eb9adb..f4a5f754e0 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,25 +1,23 @@ -# Summary +> [!IMPORTANT] +> +> 1. Make sure you have read our [contribution guidelines](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) +> 2. Ensure there is an associated issue and you have been assigned to it +> 3. Use the correct syntax to link this PR: `Fixes #`. -Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. +## Summary -> [!Tip] -> Close issue syntax: `Fixes #` or `Resolves #`, see [documentation](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) for more details. + - -# Screenshots +## Screenshots | Before | After | |--------|-------| | ... | ... | -# Checklist - -> [!IMPORTANT] -> Please review the checklist below before submitting your pull request. +## Checklist - [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) - [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) - [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. - [x] I've updated the documentation accordingly. - [x] I ran `dev/reformat`(backend) and `cd web && npx lint-staged`(frontend) to appease the lint gods - diff --git a/api/controllers/service_api/app/annotation.py b/api/controllers/service_api/app/annotation.py index 1a7f0c935b..595ae118ef 100644 --- a/api/controllers/service_api/app/annotation.py +++ b/api/controllers/service_api/app/annotation.py @@ -9,13 +9,13 @@ from fields.annotation_fields import ( annotation_fields, ) from libs.login import current_user -from models.model import App, EndUser +from models.model import App from services.annotation_service import AppAnnotationService class AnnotationReplyActionApi(Resource): @validate_app_token - def post(self, app_model: App, end_user: EndUser, action): + def post(self, app_model: App, action): parser = reqparse.RequestParser() parser.add_argument("score_threshold", required=True, type=float, location="json") parser.add_argument("embedding_provider_name", required=True, type=str, location="json") @@ -32,7 +32,7 @@ class AnnotationReplyActionApi(Resource): class AnnotationReplyActionStatusApi(Resource): @validate_app_token - def get(self, app_model: App, end_user: EndUser, job_id, action): + def get(self, app_model: App, job_id, action): job_id = str(job_id) app_annotation_job_key = "{}_app_annotation_job_{}".format(action, str(job_id)) cache_result = redis_client.get(app_annotation_job_key) @@ -50,7 +50,7 @@ class AnnotationReplyActionStatusApi(Resource): class AnnotationListApi(Resource): @validate_app_token - def get(self, app_model: App, end_user: EndUser): + def get(self, app_model: App): page = request.args.get("page", default=1, type=int) limit = request.args.get("limit", default=20, type=int) keyword = request.args.get("keyword", default="", type=str) @@ -67,7 +67,7 @@ class AnnotationListApi(Resource): @validate_app_token @marshal_with(annotation_fields) - def post(self, app_model: App, end_user: EndUser): + def post(self, app_model: App): parser = reqparse.RequestParser() parser.add_argument("question", required=True, type=str, location="json") parser.add_argument("answer", required=True, type=str, location="json") @@ -79,7 +79,7 @@ class AnnotationListApi(Resource): class AnnotationUpdateDeleteApi(Resource): @validate_app_token @marshal_with(annotation_fields) - def put(self, app_model: App, end_user: EndUser, annotation_id): + def put(self, app_model: App, annotation_id): if not current_user.is_editor: raise Forbidden() @@ -92,7 +92,7 @@ class AnnotationUpdateDeleteApi(Resource): return annotation @validate_app_token - def delete(self, app_model: App, end_user: EndUser, annotation_id): + def delete(self, app_model: App, annotation_id): if not current_user.is_editor: raise Forbidden() diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index 5212d797d8..4979f63432 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -63,7 +63,7 @@ class CotAgentRunner(BaseAgentRunner, ABC): self._instruction = self._fill_in_inputs_from_external_data_tools(instruction, inputs) iteration_step = 1 - max_iteration_steps = min(app_config.agent.max_iteration if app_config.agent else 5, 5) + 1 + max_iteration_steps = min(app_config.agent.max_iteration, 99) + 1 # convert tools into ModelRuntime Tool format tool_instances, prompt_messages_tools = self._init_prompt_tools() diff --git a/api/core/agent/entities.py b/api/core/agent/entities.py index e68b4f2356..143a3a51aa 100644 --- a/api/core/agent/entities.py +++ b/api/core/agent/entities.py @@ -82,7 +82,7 @@ class AgentEntity(BaseModel): strategy: Strategy prompt: Optional[AgentPromptEntity] = None tools: Optional[list[AgentToolEntity]] = None - max_iteration: int = 5 + max_iteration: int = 10 class AgentInvokeMessage(ToolInvokeMessage): diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index 611a55b30a..5491689ece 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -48,7 +48,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): assert app_config.agent iteration_step = 1 - max_iteration_steps = min(app_config.agent.max_iteration, 5) + 1 + max_iteration_steps = min(app_config.agent.max_iteration, 99) + 1 # continue to run until there is not any tool call function_call_state = True diff --git a/api/core/app/app_config/easy_ui_based_app/agent/manager.py b/api/core/app/app_config/easy_ui_based_app/agent/manager.py index f503543d7b..590b944c0d 100644 --- a/api/core/app/app_config/easy_ui_based_app/agent/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/agent/manager.py @@ -75,7 +75,7 @@ class AgentConfigManager: strategy=strategy, prompt=agent_prompt_entity, tools=agent_tools, - max_iteration=agent_dict.get("max_iteration", 5), + max_iteration=agent_dict.get("max_iteration", 10), ) return None diff --git a/api/core/plugin/backwards_invocation/model.py b/api/core/plugin/backwards_invocation/model.py index 5ec9620f22..17cfaf2edf 100644 --- a/api/core/plugin/backwards_invocation/model.py +++ b/api/core/plugin/backwards_invocation/model.py @@ -58,6 +58,7 @@ class PluginModelBackwardsInvocation(BaseBackwardsInvocation): LLMNode.deduct_llm_quota( tenant_id=tenant.id, model_instance=model_instance, usage=chunk.delta.usage ) + chunk.prompt_messages = [] yield chunk return handle() @@ -68,7 +69,7 @@ class PluginModelBackwardsInvocation(BaseBackwardsInvocation): def handle_non_streaming(response: LLMResult) -> Generator[LLMResultChunk, None, None]: yield LLMResultChunk( model=response.model, - prompt_messages=response.prompt_messages, + prompt_messages=[], system_fingerprint=response.system_fingerprint, delta=LLMResultChunkDelta( index=0, diff --git a/api/core/plugin/impl/base.py b/api/core/plugin/impl/base.py index 591e7b0525..7b9592bff3 100644 --- a/api/core/plugin/impl/base.py +++ b/api/core/plugin/impl/base.py @@ -6,6 +6,7 @@ from typing import TypeVar import requests from pydantic import BaseModel +from requests.exceptions import HTTPError from yarl import URL from configs import dify_config @@ -136,12 +137,31 @@ class BasePluginClient: """ Make a request to the plugin daemon inner API and return the response as a model. """ - response = self._request(method, path, headers, data, params, files) - json_response = response.json() - if transformer: - json_response = transformer(json_response) + try: + response = self._request(method, path, headers, data, params, files) + response.raise_for_status() + except HTTPError as e: + msg = f"Failed to request plugin daemon, status: {e.response.status_code}, url: {path}" + logging.exception(msg) + raise e + except Exception as e: + msg = f"Failed to request plugin daemon, url: {path}" + logging.exception(msg) + raise ValueError(msg) from e + + try: + json_response = response.json() + if transformer: + json_response = transformer(json_response) + rep = PluginDaemonBasicResponse[type](**json_response) # type: ignore + except Exception: + msg = ( + f"Failed to parse response from plugin daemon to PluginDaemonBasicResponse [{str(type.__name__)}]," + f" url: {path}" + ) + logging.exception(msg) + raise ValueError(msg) - rep = PluginDaemonBasicResponse[type](**json_response) # type: ignore if rep.code != 0: try: error = PluginDaemonError(**json.loads(rep.message)) diff --git a/docker/.env.example b/docker/.env.example index 9d68527796..ac9536be03 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -802,7 +802,7 @@ MAX_TOOLS_NUM=10 MAX_PARALLEL_LIMIT=10 # The maximum number of iterations for agent setting -MAX_ITERATIONS_NUM=5 +MAX_ITERATIONS_NUM=99 # ------------------------------ # Environment Variables for web Service diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index 256a6131ae..74a7b87bf9 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -75,7 +75,7 @@ services: LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} - MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-5} + MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index a13f115cd2..41e86d015f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -354,7 +354,7 @@ x-shared-env: &shared-api-worker-env LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} - MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-5} + MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000} PGUSER: ${PGUSER:-${DB_USERNAME}} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-${DB_PASSWORD}} @@ -574,7 +574,7 @@ services: LOOP_NODE_MAX_COUNT: ${LOOP_NODE_MAX_COUNT:-100} MAX_TOOLS_NUM: ${MAX_TOOLS_NUM:-10} MAX_PARALLEL_LIMIT: ${MAX_PARALLEL_LIMIT:-10} - MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-5} + MAX_ITERATIONS_NUM: ${MAX_ITERATIONS_NUM:-99} ENABLE_WEBSITE_JINAREADER: ${ENABLE_WEBSITE_JINAREADER:-true} ENABLE_WEBSITE_FIRECRAWL: ${ENABLE_WEBSITE_FIRECRAWL:-true} ENABLE_WEBSITE_WATERCRAWL: ${ENABLE_WEBSITE_WATERCRAWL:-true} diff --git a/docker/middleware.env.example b/docker/middleware.env.example index 2437026eec..ba6859885b 100644 --- a/docker/middleware.env.example +++ b/docker/middleware.env.example @@ -109,7 +109,7 @@ EXPOSE_PLUGIN_DEBUGGING_HOST=localhost EXPOSE_PLUGIN_DEBUGGING_PORT=5003 PLUGIN_DIFY_INNER_API_KEY=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1 -PLUGIN_DIFY_INNER_API_URL=http://api:5001 +PLUGIN_DIFY_INNER_API_URL=http://host.docker.internal:5001 MARKETPLACE_ENABLED=true MARKETPLACE_API_URL=https://marketplace.dify.ai diff --git a/web/.env.example b/web/.env.example index 51631c2437..78b4f33e8c 100644 --- a/web/.env.example +++ b/web/.env.example @@ -50,7 +50,7 @@ NEXT_PUBLIC_MAX_TOOLS_NUM=10 NEXT_PUBLIC_MAX_PARALLEL_LIMIT=10 # The maximum number of iterations for agent setting -NEXT_PUBLIC_MAX_ITERATIONS_NUM=5 +NEXT_PUBLIC_MAX_ITERATIONS_NUM=99 NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER=true NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL=true diff --git a/web/app/account/header.tsx b/web/app/account/header.tsx index 11b6beec08..d033bfab61 100644 --- a/web/app/account/header.tsx +++ b/web/app/account/header.tsx @@ -6,10 +6,12 @@ import Button from '../components/base/button' import Avatar from './avatar' import DifyLogo from '@/app/components/base/logo/dify-logo' import { useCallback } from 'react' +import { useGlobalPublicStore } from '@/context/global-public-context' const Header = () => { const { t } = useTranslation() const router = useRouter() + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const back = useCallback(() => { router.back() @@ -19,7 +21,13 @@ const Header = () => {
- + {systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo + ? Dify logo + : }

{t('common.account.account')}

diff --git a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx index 5fde657f68..fd317ccf91 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx @@ -148,10 +148,12 @@ const Sidebar = ({ isPanel }: Props) => { 'flex shrink-0 items-center gap-1.5 px-1', )}>
{t('share.chat.poweredBy')}
- {systemFeatures.branding.enabled ? ( - logo - ) : ( - ) + { + systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : appData?.custom_config?.replace_webapp_logo + ? logo + : }
)} diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index c6c02a4d44..95975e29e7 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -13,6 +13,7 @@ import Divider from '@/app/components/base/divider' import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' import DifyLogo from '@/app/components/base/logo/dify-logo' import cn from '@/utils/classnames' +import { useGlobalPublicStore } from '@/context/global-public-context' export type IHeaderProps = { isMobile?: boolean @@ -42,6 +43,7 @@ const Header: FC = ({ const [parentOrigin, setParentOrigin] = useState('') const [showToggleExpandButton, setShowToggleExpandButton] = useState(false) const [expanded, setExpanded] = useState(false) + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const handleMessageReceived = useCallback((event: MessageEvent) => { let currentParentOrigin = parentOrigin @@ -85,12 +87,13 @@ const Header: FC = ({ 'flex shrink-0 items-center gap-1.5 px-2', )}>
{t('share.chat.poweredBy')}
- {appData?.custom_config?.replace_webapp_logo && ( - logo - )} - {!appData?.custom_config?.replace_webapp_logo && ( - - )} + { + systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : appData?.custom_config?.replace_webapp_logo + ? logo + : + }
)}
diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index ffcb128c8f..002d142542 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -22,6 +22,7 @@ import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrappe import DifyLogo from '@/app/components/base/logo/dify-logo' import cn from '@/utils/classnames' import useDocumentTitle from '@/hooks/use-document-title' +import { useGlobalPublicStore } from '@/context/global-public-context' const Chatbot = () => { const { @@ -37,6 +38,7 @@ const Chatbot = () => { themeBuilder, } = useEmbeddedChatbotContext() const { t } = useTranslation() + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const customConfig = appData?.custom_config const site = appData?.site @@ -115,12 +117,13 @@ const Chatbot = () => { 'flex shrink-0 items-center gap-1.5 px-2', )}>
{t('share.chat.poweredBy')}
- {appData?.custom_config?.replace_webapp_logo && ( - logo - )} - {!appData?.custom_config?.replace_webapp_logo && ( - - )} + { + systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : appData?.custom_config?.replace_webapp_logo + ? logo + : + } )} diff --git a/web/app/components/base/logo/dify-logo.tsx b/web/app/components/base/logo/dify-logo.tsx index 9e8f077372..5369144e1c 100644 --- a/web/app/components/base/logo/dify-logo.tsx +++ b/web/app/components/base/logo/dify-logo.tsx @@ -3,7 +3,6 @@ import type { FC } from 'react' import classNames from '@/utils/classnames' import useTheme from '@/hooks/use-theme' import { basePath } from '@/utils/var' -import { useGlobalPublicStore } from '@/context/global-public-context' export type LogoStyle = 'default' | 'monochromeWhite' export const logoPathMap: Record = { @@ -32,18 +31,12 @@ const DifyLogo: FC = ({ }) => { const { theme } = useTheme() const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style - const { systemFeatures } = useGlobalPublicStore() - const hasBrandingLogo = Boolean(systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo) - - let src = `${basePath}${logoPathMap[themedStyle]}` - if (hasBrandingLogo) - src = systemFeatures.branding.workspace_logo return ( {hasBrandingLogo ) } diff --git a/web/app/components/custom/custom-web-app-brand/index.tsx b/web/app/components/custom/custom-web-app-brand/index.tsx index 444df98f24..ea2f44caea 100644 --- a/web/app/components/custom/custom-web-app-brand/index.tsx +++ b/web/app/components/custom/custom-web-app-brand/index.tsx @@ -24,6 +24,7 @@ import { } from '@/service/common' import { useAppContext } from '@/context/app-context' import cn from '@/utils/classnames' +import { useGlobalPublicStore } from '@/context/global-public-context' const ALLOW_FILE_EXTENSIONS = ['svg', 'png'] @@ -39,6 +40,7 @@ const CustomWebAppBrand = () => { const [fileId, setFileId] = useState('') const [imgKey, setImgKey] = useState(Date.now()) const [uploadProgress, setUploadProgress] = useState(0) + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const isSandbox = enableBilling && plan.type === Plan.sandbox const uploading = uploadProgress > 0 && uploadProgress < 100 const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || '' @@ -128,7 +130,7 @@ const CustomWebAppBrand = () => {
{t('custom.webapp.changeLogoTip')}
- {(uploadDisabled || (!webappLogo && !webappBrandRemoved)) && ( + {(!uploadDisabled && webappLogo && !webappBrandRemoved) && ( <>
- + {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : } +
Version {langeniusVersionInfo?.current_version}
© {dayjs().year()} LangGenius, Inc., Contributors.
diff --git a/web/app/components/header/index.tsx b/web/app/components/header/index.tsx index 6e8d1704dd..a9c26e0070 100644 --- a/web/app/components/header/index.tsx +++ b/web/app/components/header/index.tsx @@ -21,6 +21,7 @@ import { useModalContext } from '@/context/modal-context' import PlanBadge from './plan-badge' import LicenseNav from './license-env' import { Plan } from '../billing/type' +import { useGlobalPublicStore } from '@/context/global-public-context' const navClassName = ` flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl @@ -36,6 +37,7 @@ const Header = () => { const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false) const { enableBilling, plan } = useProviderContext() const { setShowPricingModal, setShowAccountSettingModal } = useModalContext() + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const isFreePlan = plan.type === Plan.sandbox const handlePlanClick = useCallback(() => { if (isFreePlan) @@ -61,7 +63,13 @@ const Header = () => { !isMobile &&
- + {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : }
/
@@ -76,7 +84,13 @@ const Header = () => { {isMobile && (
- + {systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : }
/
{enableBilling ? : } diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 5450fa7ce6..6fd6d17278 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -641,11 +641,13 @@ const TextGeneration: FC = ({ !isPC && resultExisted && 'rounded-b-2xl border-b-[0.5px] border-divider-regular', )}>
{t('share.chat.poweredBy')}
- {systemFeatures.branding.enabled ? ( - logo - ) : ( - - )} + { + systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo + ? logo + : customConfig?.replace_webapp_logo + ? logo + : + }
)}
diff --git a/web/app/signin/_header.tsx b/web/app/signin/_header.tsx index 5e85a8d306..0efd30b6cc 100644 --- a/web/app/signin/_header.tsx +++ b/web/app/signin/_header.tsx @@ -7,6 +7,7 @@ import { languages } from '@/i18n/language' import type { Locale } from '@/i18n' import I18n from '@/context/i18n' import dynamic from 'next/dynamic' +import { useGlobalPublicStore } from '@/context/global-public-context' // Avoid rendering the logo and theme selector on the server const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { @@ -20,10 +21,17 @@ const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector const Header = () => { const { locale, setLocaleOnClient } = useContext(I18n) + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) return (
- + {systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo + ? logo + : }