mirror of https://github.com/langgenius/dify.git
Merge remote-tracking branch 'origin/feat/trigger' into feat/trigger
This commit is contained in:
commit
aea3fc6281
|
|
@ -1056,15 +1056,23 @@ class DraftWorkflowTriggerNodeApi(Resource):
|
|||
raise e
|
||||
if not event:
|
||||
return jsonable_encoder({"status": "waiting", "retry_in": LISTENING_RETRY_IN})
|
||||
|
||||
workflow_args = dict(event.workflow_args or {})
|
||||
raw_files = workflow_args.get("files")
|
||||
files = _parse_file(draft_workflow, raw_files if isinstance(raw_files, list) else None)
|
||||
if node_type == NodeType.TRIGGER_WEBHOOK:
|
||||
user_inputs = workflow_args.get("inputs") or {}
|
||||
else:
|
||||
user_inputs = workflow_args
|
||||
try:
|
||||
node_execution = workflow_service.run_draft_workflow_node(
|
||||
app_model=app_model,
|
||||
draft_workflow=draft_workflow,
|
||||
node_id=node_id,
|
||||
user_inputs=event.workflow_args,
|
||||
user_inputs=user_inputs,
|
||||
account=current_user,
|
||||
query="",
|
||||
files=[],
|
||||
files=files,
|
||||
)
|
||||
return jsonable_encoder(node_execution)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -114,6 +114,24 @@ class PluginIconApi(Resource):
|
|||
return send_file(io.BytesIO(icon_bytes), mimetype=mimetype, max_age=icon_cache_max_age)
|
||||
|
||||
|
||||
class PluginAssetApi(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
def get(self):
|
||||
req = reqparse.RequestParser()
|
||||
req.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
|
||||
req.add_argument("file_name", type=str, required=True, location="args")
|
||||
args = req.parse_args()
|
||||
|
||||
current_user, tenant_id = current_account_with_tenant()
|
||||
try:
|
||||
binary = PluginService.extract_asset(tenant_id, args["plugin_unique_identifier"], args["file_name"])
|
||||
return send_file(io.BytesIO(binary), mimetype="application/octet-stream")
|
||||
except PluginDaemonClientSideError as e:
|
||||
raise ValueError(e)
|
||||
|
||||
|
||||
@console_ns.route("/workspaces/current/plugin/upload/pkg")
|
||||
class PluginUploadFromPkgApi(Resource):
|
||||
@setup_required
|
||||
|
|
@ -688,3 +706,25 @@ class PluginAutoUpgradeExcludePluginApi(Resource):
|
|||
args = req.parse_args()
|
||||
|
||||
return jsonable_encoder({"success": PluginAutoUpgradeService.exclude_plugin(tenant_id, args["plugin_id"])})
|
||||
|
||||
|
||||
@console_ns.route("/workspaces/current/plugin/readme")
|
||||
class PluginReadmeApi(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
def get(self):
|
||||
current_user, tenant_id = current_account_with_tenant()
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument("plugin_unique_identifier", type=str, required=True, location="args")
|
||||
parser.add_argument("language", type=str, required=False, location="args")
|
||||
args = parser.parse_args()
|
||||
return jsonable_encoder(
|
||||
{
|
||||
"readme": PluginService.fetch_plugin_readme(
|
||||
tenant_id,
|
||||
args["plugin_unique_identifier"],
|
||||
args.get("language", "en-US")
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -252,3 +252,7 @@ class CredentialType(enum.StrEnum):
|
|||
return cls.UNAUTHORIZED
|
||||
else:
|
||||
raise ValueError(f"Invalid credential type: {credential_type}")
|
||||
|
||||
class PluginReadmeResponse(BaseModel):
|
||||
content: str = Field(description="The readme of the plugin.")
|
||||
language: str = Field(description="The language of the readme.")
|
||||
|
|
|
|||
|
|
@ -10,3 +10,9 @@ class PluginAssetManager(BasePluginClient):
|
|||
if response.status_code != 200:
|
||||
raise ValueError(f"can not found asset {id}")
|
||||
return response.content
|
||||
|
||||
def extract_asset(self, tenant_id: str, plugin_unique_identifier: str, filename: str) -> bytes:
|
||||
response = self._request(method="GET", path=f"plugin/{tenant_id}/asset/{plugin_unique_identifier}")
|
||||
if response.status_code != 200:
|
||||
raise ValueError(f"can not found asset {plugin_unique_identifier}, {str(response.status_code)}")
|
||||
return response.content
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from collections.abc import Sequence
|
||||
|
||||
from requests import HTTPError
|
||||
|
||||
from core.plugin.entities.bundle import PluginBundleDependency
|
||||
from core.plugin.entities.plugin import (
|
||||
MissingPluginDependency,
|
||||
|
|
@ -13,12 +15,35 @@ from core.plugin.entities.plugin_daemon import (
|
|||
PluginInstallTask,
|
||||
PluginInstallTaskStartResponse,
|
||||
PluginListResponse,
|
||||
PluginReadmeResponse,
|
||||
)
|
||||
from core.plugin.impl.base import BasePluginClient
|
||||
from models.provider_ids import GenericProviderID
|
||||
|
||||
|
||||
class PluginInstaller(BasePluginClient):
|
||||
def fetch_plugin_readme(self, tenant_id: str, plugin_unique_identifier: str, language: str) -> str:
|
||||
"""
|
||||
Fetch plugin readme
|
||||
"""
|
||||
try:
|
||||
response = self._request_with_plugin_daemon_response(
|
||||
"GET",
|
||||
f"plugin/{tenant_id}/management/fetch/readme",
|
||||
PluginReadmeResponse,
|
||||
params={
|
||||
"tenant_id":tenant_id,
|
||||
"plugin_unique_identifier": plugin_unique_identifier,
|
||||
"language": language
|
||||
}
|
||||
)
|
||||
return response.content
|
||||
except HTTPError as e:
|
||||
message = e.args[0]
|
||||
if "404" in message:
|
||||
return ""
|
||||
raise e
|
||||
|
||||
def fetch_plugin_by_identifier(
|
||||
self,
|
||||
tenant_id: str,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ IMPORT_INFO_REDIS_KEY_PREFIX = "app_import_info:"
|
|||
CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:"
|
||||
IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes
|
||||
DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB
|
||||
CURRENT_DSL_VERSION = "0.4.0"
|
||||
CURRENT_DSL_VERSION = "0.5.0"
|
||||
|
||||
|
||||
class ImportMode(StrEnum):
|
||||
|
|
|
|||
|
|
@ -193,6 +193,11 @@ class PluginService:
|
|||
mime_type, _ = guess_type(asset_file)
|
||||
return manager.fetch_asset(tenant_id, asset_file), mime_type or "application/octet-stream"
|
||||
|
||||
@staticmethod
|
||||
def extract_asset(tenant_id: str, plugin_unique_identifier: str, file_name: str) -> bytes:
|
||||
manager = PluginAssetManager()
|
||||
return manager.extract_asset(tenant_id, plugin_unique_identifier, file_name)
|
||||
|
||||
@staticmethod
|
||||
def check_plugin_unique_identifier(tenant_id: str, plugin_unique_identifier: str) -> bool:
|
||||
"""
|
||||
|
|
@ -510,3 +515,11 @@ class PluginService:
|
|||
"""
|
||||
manager = PluginInstaller()
|
||||
return manager.check_tools_existence(tenant_id, provider_ids)
|
||||
|
||||
@staticmethod
|
||||
def fetch_plugin_readme(tenant_id: str, plugin_unique_identifier: str, language: str) -> str:
|
||||
"""
|
||||
Fetch plugin readme
|
||||
"""
|
||||
manager = PluginInstaller()
|
||||
return manager.fetch_plugin_readme(tenant_id, plugin_unique_identifier, language)
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ function AppCard({
|
|||
</div>
|
||||
<div
|
||||
className="cursor-pointer text-xs font-normal text-text-accent hover:underline"
|
||||
onClick={() => window.open(docLink('/guides/workflow/node/start'), '_blank')}
|
||||
onClick={() => window.open(docLink('/guides/workflow/node/user-input'), '_blank')}
|
||||
>
|
||||
{t('appOverview.overview.appInfo.enableTooltip.learnMore')}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { canFindTool } from '@/utils'
|
|||
import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status'
|
||||
import BlockIcon from '@/app/components/workflow/block-icon'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export type ITriggerCardProps = {
|
||||
appInfo: AppDetailResponse & Partial<AppSSO>
|
||||
|
|
@ -83,6 +84,7 @@ const getTriggerIcon = (trigger: AppTrigger, triggerPlugins: any[]) => {
|
|||
|
||||
function TriggerCard({ appInfo, onToggleResult }: ITriggerCardProps) {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const appId = appInfo.id
|
||||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
const { data: triggersResponse, isLoading } = useAppTriggers(appId)
|
||||
|
|
@ -136,10 +138,6 @@ function TriggerCard({ appInfo, onToggleResult }: ITriggerCardProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const handleLearnMoreClick = () => {
|
||||
console.log('Learn about Triggers clicked')
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="w-full max-w-full rounded-xl border-l-[0.5px] border-t border-effects-highlight">
|
||||
|
|
@ -209,8 +207,9 @@ function TriggerCard({ appInfo, onToggleResult }: ITriggerCardProps) {
|
|||
<div className="system-xs-regular leading-4 text-text-tertiary">
|
||||
{t('appOverview.overview.triggerInfo.triggerStatusDescription')}{' '}
|
||||
<Link
|
||||
href="#"
|
||||
onClick={handleLearnMoreClick}
|
||||
href={docLink('/guides/workflow/node/trigger')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-text-accent hover:underline"
|
||||
>
|
||||
{t('appOverview.overview.triggerInfo.learnAboutTriggers')}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 3.22388L3.86194 7.36193L4.80475 8.30473L8 5.10949L11.1953 8.30473L12.1381 7.36193L8 3.22388ZM8 6.99046L3.86194 11.1285L4.80475 12.0713L8 8.87606L11.1953 12.0713L12.1381 11.1285L8 6.99046Z" fill="#676F83"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M8 3.22388L3.86194 7.36193L4.80475 8.30473L8 5.10949L11.1953 8.30473L12.1381 7.36193L8 3.22388ZM8 6.99046L3.86194 11.1285L4.80475 12.0713L8 8.87606L11.1953 12.0713L12.1381 11.1285L8 6.99046Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ArrowUpDoubleLine"
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ArrowUpDoubleLine.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = (
|
||||
{
|
||||
ref,
|
||||
...props
|
||||
}: React.SVGProps<SVGSVGElement> & {
|
||||
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
|
||||
},
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />
|
||||
|
||||
Icon.displayName = 'ArrowUpDoubleLine'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as ArrowDownDoubleLine } from './ArrowDownDoubleLine'
|
||||
export { default as ArrowDownRoundFill } from './ArrowDownRoundFill'
|
||||
export { default as ArrowUpDoubleLine } from './ArrowUpDoubleLine'
|
||||
export { default as ChevronDown } from './ChevronDown'
|
||||
export { default as HighPriority } from './HighPriority'
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
|||
<div className="body-xs-regular leading-4 text-text-tertiary">
|
||||
{t('workflow.onboarding.description')}{' '}
|
||||
<a
|
||||
href={docLink('guides/workflow/node/start')}
|
||||
href={docLink('/guides/workflow/node/start')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-text-accent-hover cursor-pointer text-text-accent underline"
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ const AllTools = ({
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
{(hasToolsListContent || hasPluginContent) && (
|
||||
{(hasToolsListContent || enable_marketplace) && (
|
||||
<>
|
||||
<div className='px-3 pb-1 pt-2'>
|
||||
<span className='system-xs-medium text-text-primary'>{t('tools.allTools')}</span>
|
||||
|
|
@ -249,7 +249,7 @@ const AllTools = ({
|
|||
isShowRAGRecommendations={isShowRAGRecommendations}
|
||||
/>
|
||||
)}
|
||||
{hasPluginContent && (
|
||||
{enable_marketplace && (
|
||||
<PluginList
|
||||
ref={pluginRef}
|
||||
wrapElemRef={wrapElemRef as RefObject<HTMLElement>}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { ViewType } from './view-type-select'
|
|||
import Tools from './tools'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import Action from '@/app/components/workflow/block-selector/market-place-plugin/action'
|
||||
import { ArrowDownDoubleLine, ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/arrows'
|
||||
import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows'
|
||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||
|
||||
const MAX_RECOMMENDED_COUNT = 15
|
||||
|
|
@ -119,7 +119,9 @@ const FeaturedTools = ({
|
|||
|
||||
const totalVisible = visibleInstalledProviders.length + visibleUninstalledPlugins.length
|
||||
const maxAvailable = Math.min(MAX_RECOMMENDED_COUNT, installedProviders.length + uninstalledPlugins.length)
|
||||
const showMore = totalVisible < maxAvailable
|
||||
const hasMoreToShow = totalVisible < maxAvailable
|
||||
const canToggleVisibility = maxAvailable > INITIAL_VISIBLE_COUNT
|
||||
const isExpanded = canToggleVisibility && !hasMoreToShow
|
||||
const showEmptyState = !isLoading && totalVisible === 0
|
||||
|
||||
return (
|
||||
|
|
@ -183,19 +185,28 @@ const FeaturedTools = ({
|
|||
</>
|
||||
)}
|
||||
|
||||
{!isLoading && totalVisible > 0 && showMore && (
|
||||
{!isLoading && totalVisible > 0 && canToggleVisibility && (
|
||||
<div
|
||||
className='group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary'
|
||||
onClick={() => {
|
||||
setVisibleCount(count => Math.min(count + INITIAL_VISIBLE_COUNT, maxAvailable))
|
||||
setVisibleCount((count) => {
|
||||
if (count >= maxAvailable)
|
||||
return INITIAL_VISIBLE_COUNT
|
||||
|
||||
return Math.min(count + INITIAL_VISIBLE_COUNT, maxAvailable)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary'>
|
||||
<RiMoreLine className='size-4 group-hover:hidden' />
|
||||
<ArrowDownDoubleLine className='hidden size-4 group-hover:block' />
|
||||
{isExpanded ? (
|
||||
<ArrowUpDoubleLine className='hidden size-4 group-hover:block' />
|
||||
) : (
|
||||
<ArrowDownDoubleLine className='hidden size-4 group-hover:block' />
|
||||
)}
|
||||
</div>
|
||||
<div className='system-xs-regular'>
|
||||
{t('workflow.tabs.showMoreFeatured')}
|
||||
{t(isExpanded ? 'workflow.tabs.showLessFeatured' : 'workflow.tabs.showMoreFeatured')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ const convertJsonSchemaToField = (schema: any, schemaTypeDefinitions?: SchemaTyp
|
|||
const metaData = genNodeMetaData({
|
||||
sort: 1,
|
||||
type: BlockEnum.TriggerPlugin,
|
||||
helpLinkUri: 'plugin-trigger',
|
||||
isStart: true,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ const validateVisualConfig = (payload: ScheduleTriggerNodeType, t: any): string
|
|||
const metaData = genNodeMetaData({
|
||||
sort: 2,
|
||||
type: BlockEnum.TriggerSchedule,
|
||||
helpLinkUri: 'schedule-trigger',
|
||||
isStart: true,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { createWebhookRawVariable } from './utils/raw-variable'
|
|||
const metaData = genNodeMetaData({
|
||||
sort: 3,
|
||||
type: BlockEnum.TriggerWebhook,
|
||||
helpLinkUri: 'webhook-trigger',
|
||||
isStart: true,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ const translation = {
|
|||
'searchDataSource': 'Search Data Source',
|
||||
'featuredTools': 'Featured',
|
||||
'showMoreFeatured': 'Show more',
|
||||
'showLessFeatured': 'Show less',
|
||||
'installed': 'Installed',
|
||||
'pluginByAuthor': 'By {{author}}',
|
||||
'usePlugin': 'Select tool',
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ const translation = {
|
|||
'start': '开始',
|
||||
'featuredTools': '精选推荐',
|
||||
'showMoreFeatured': '查看更多',
|
||||
'showLessFeatured': '收起',
|
||||
'installed': '已安装',
|
||||
'pluginByAuthor': '来自 {{author}}',
|
||||
'usePlugin': '选择工具',
|
||||
|
|
|
|||
Loading…
Reference in New Issue