Merge branch 'langgenius:main' into tracing-weave

pull/14262/head
Bharat Ramanathan 1 year ago committed by GitHub
commit 58dfa551c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -75,9 +75,7 @@ Dify est une plateforme de développement d'applications LLM open source. Son in
**4. Pipeline RAG** :
Des capacités RAG étendues qui couvrent tout, de l'ingestion de documents à la récupération, avec un support prêt à l'emploi pour l'extraction de texte à partir de PDF, PPT et autres formats de document courants.
**5. Capac
ités d'agent**:
**5. Capacités d'agent** :
Vous pouvez définir des agents basés sur l'appel de fonction LLM ou ReAct, et ajouter des outils pré-construits ou personnalisés pour l'agent. Dify fournit plus de 50 outils intégrés pour les agents d'IA, tels que la recherche Google, DALL·E, Stable Diffusion et WolframAlpha.
**6. LLMOps** :

@ -1,6 +1,3 @@
from collections.abc import Mapping, Sequence
from typing import Any
from core.workflow.entities.node_entities import NodeRunResult
from core.workflow.nodes.base import BaseNode
from core.workflow.nodes.enums import NodeType
@ -36,16 +33,3 @@ class VariableAggregatorNode(BaseNode[VariableAssignerNodeData]):
break
return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, outputs=outputs, inputs=inputs)
@classmethod
def _extract_variable_selector_to_variable_mapping(
cls, *, graph_config: Mapping[str, Any], node_id: str, node_data: VariableAssignerNodeData
) -> Mapping[str, Sequence[str]]:
"""
Extract variable selector to variable mapping
:param graph_config: graph config
:param node_id: node id
:param node_data: node data
:return:
"""
return {}

@ -59,8 +59,8 @@ const Apps = () => {
const [activeTab, setActiveTab] = useTabSearchParams({
defaultTab: 'all',
})
const { query: { tagIDs = [], keywords = '' }, setQuery } = useAppsQueryState()
const [isCreatedByMe, setIsCreatedByMe] = useState(false)
const { query: { tagIDs = [], keywords = '', isCreatedByMe: queryIsCreatedByMe = false }, setQuery } = useAppsQueryState()
const [isCreatedByMe, setIsCreatedByMe] = useState(queryIsCreatedByMe)
const [tagFilterValue, setTagFilterValue] = useState<string[]>(tagIDs)
const [searchKeywords, setSearchKeywords] = useState(keywords)
const setKeywords = useCallback((keywords: string) => {
@ -126,6 +126,12 @@ const Apps = () => {
handleTagsUpdate()
}
const handleCreatedByMeChange = useCallback(() => {
const newValue = !isCreatedByMe
setIsCreatedByMe(newValue)
setQuery(prev => ({ ...prev, isCreatedByMe: newValue }))
}, [isCreatedByMe, setQuery])
return (
<>
<div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
@ -139,7 +145,7 @@ const Apps = () => {
className='mr-2'
label={t('app.showMyCreatedAppsOnly')}
isChecked={isCreatedByMe}
onChange={() => setIsCreatedByMe(!isCreatedByMe)}
onChange={handleCreatedByMeChange}
/>
<TagFilter type='app' value={tagFilterValue} onChange={handleTagsChange} />
<Input

@ -4,18 +4,20 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
type AppsQuery = {
tagIDs?: string[]
keywords?: string
isCreatedByMe?: boolean
}
// Parse the query parameters from the URL search string.
function parseParams(params: ReadonlyURLSearchParams): AppsQuery {
const tagIDs = params.get('tagIDs')?.split(';')
const keywords = params.get('keywords') || undefined
return { tagIDs, keywords }
const isCreatedByMe = params.get('isCreatedByMe') === 'true'
return { tagIDs, keywords, isCreatedByMe }
}
// Update the URL search string with the given query parameters.
function updateSearchParams(query: AppsQuery, current: URLSearchParams) {
const { tagIDs, keywords } = query || {}
const { tagIDs, keywords, isCreatedByMe } = query || {}
if (tagIDs && tagIDs.length > 0)
current.set('tagIDs', tagIDs.join(';'))
@ -26,6 +28,11 @@ function updateSearchParams(query: AppsQuery, current: URLSearchParams) {
current.set('keywords', keywords)
else
current.delete('keywords')
if (isCreatedByMe)
current.set('isCreatedByMe', 'true')
else
current.delete('isCreatedByMe')
}
function useAppsQueryState() {

@ -243,7 +243,7 @@ const FileUploader = ({
}, [handleDrop])
return (
<div className="mb-5 w-[640px]">
<div className="mb-5 max-w-[640px]">
{!hideUpload && (
<input
ref={fileUploader}

@ -14,7 +14,7 @@
}
.dataSourceItem {
@apply box-border relative grow shrink-0 flex items-center p-3 h-14 bg-white rounded-xl cursor-pointer;
@apply w-full box-border relative flex items-center p-3 h-14 bg-white rounded-xl cursor-pointer;
border: 0.5px solid #EAECF0;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
font-weight: 500;
@ -64,7 +64,7 @@
}
.datasetIcon {
@apply flex mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat;
@apply flex shrink-0 mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat;
background-color: #F5FAFF;
background-image: url(../assets/file.svg);
background-size: 16px;

@ -137,7 +137,7 @@ const StepOne = ({
}
{
shouldShowDataSourceTypeList && (
<div className='flex items-center mb-8 flex-wrap gap-4'>
<div className='grid grid-cols-3 mb-8 gap-4'>
<div
className={cn(
s.dataSourceItem,
@ -153,7 +153,12 @@ const StepOne = ({
}}
>
<span className={cn(s.datasetIcon)} />
<span
title={t('datasetCreation.stepOne.dataSourceType.file')}
className='truncate'
>
{t('datasetCreation.stepOne.dataSourceType.file')}
</span>
</div>
<div
className={cn(
@ -170,7 +175,12 @@ const StepOne = ({
}}
>
<span className={cn(s.datasetIcon, s.notion)} />
<span
title={t('datasetCreation.stepOne.dataSourceType.notion')}
className='truncate'
>
{t('datasetCreation.stepOne.dataSourceType.notion')}
</span>
</div>
<div
className={cn(
@ -181,7 +191,12 @@ const StepOne = ({
onClick={() => changeType(DataSourceType.WEB)}
>
<span className={cn(s.datasetIcon, s.web)} />
<span
title={t('datasetCreation.stepOne.dataSourceType.web')}
className='truncate'
>
{t('datasetCreation.stepOne.dataSourceType.web')}
</span>
</div>
</div>
)

@ -1,8 +1,8 @@
'use client'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import { usePathname, useSearchParams, useSelectedLayoutSegment } from 'next/navigation'
import type { INavSelectorProps } from './nav-selector'
import NavSelector from './nav-selector'
import classNames from '@/utils/classnames'
@ -35,6 +35,14 @@ const Nav = ({
const [hovered, setHovered] = useState(false)
const segment = useSelectedLayoutSegment()
const isActivated = Array.isArray(activeSegment) ? activeSegment.includes(segment!) : segment === activeSegment
const pathname = usePathname()
const searchParams = useSearchParams()
const [linkLastSearchParams, setLinkLastSearchParams] = useState('')
useEffect(() => {
if (pathname === link)
setLinkLastSearchParams(searchParams.toString())
}, [pathname, searchParams])
return (
<div className={`
@ -42,7 +50,7 @@ const Nav = ({
${isActivated && 'bg-components-main-nav-nav-button-bg-active shadow-md font-semibold'}
${!curNav && !isActivated && 'hover:bg-components-main-nav-nav-button-bg-hover'}
`}>
<Link href={link}>
<Link href={link + (linkLastSearchParams && `?${linkLastSearchParams}`)}>
<div
onClick={() => setAppDetail()}
className={classNames(`

@ -23,17 +23,15 @@ const useRefreshPluginList = () => {
// installed list
invalidateInstalledPluginList()
if (!manifest) return
// tool page, tool select
if (PluginType.tool.includes(manifest.category) || refreshAllType) {
if ((manifest && PluginType.tool.includes(manifest.category)) || refreshAllType) {
invalidateAllToolProviders()
invalidateAllBuiltInTools()
// TODO: update suggested tools. It's a function in hook useMarketplacePlugins,handleUpdatePlugins
}
// model select
if (PluginType.model.includes(manifest.category) || refreshAllType) {
if ((manifest && PluginType.model.includes(manifest.category)) || refreshAllType) {
refreshModelProviders()
refetchLLMModelList()
refetchEmbeddingModelList()
@ -41,7 +39,7 @@ const useRefreshPluginList = () => {
}
// agent select
if (PluginType.agent.includes(manifest.category) || refreshAllType)
if ((manifest && PluginType.agent.includes(manifest.category)) || refreshAllType)
invalidateStrategyProviders()
},
}

@ -148,15 +148,15 @@ const translation = {
},
avgSessionInteractions: {
title: '平均会话互动数',
explanation: '反每个会话用户的持续沟通次数,如果用户与 AI 问答了 10 轮,即为 10。该指标反映了用户粘性。仅在对话型应用提供。',
explanation: '反每个会话用户的持续沟通次数,如果用户与 AI 问答了 10 轮,即为 10。该指标反映了用户粘性。仅在对话型应用提供。',
},
avgUserInteractions: {
title: '平均用户调用次数',
explanation: '反每天用户的使用次数。该指标反映了用户粘性。',
explanation: '反每天用户的使用次数。该指标反映了用户粘性。',
},
userSatisfactionRate: {
title: '用户满意度',
explanation: '每 1000 条消息的点赞数。反了用户对回答十分满意的比例。',
explanation: '每 1000 条消息的点赞数。反了用户对回答十分满意的比例。',
},
avgResponseTime: {
title: '平均响应时间',

@ -148,15 +148,15 @@ const translation = {
},
avgSessionInteractions: {
title: '平均會話互動數',
explanation: '反每個會話使用者的持續溝通次數,如果使用者與 AI 問答了 10 輪,即為 10。該指標反映了使用者粘性。僅在對話型應用提供。',
explanation: '反每個會話使用者的持續溝通次數,如果使用者與 AI 問答了 10 輪,即為 10。該指標反映了使用者粘性。僅在對話型應用提供。',
},
avgUserInteractions: {
title: '平均使用者呼叫次數',
explanation: '反每天使用者的使用次數。該指標反映了使用者粘性。',
explanation: '反每天使用者的使用次數。該指標反映了使用者粘性。',
},
userSatisfactionRate: {
title: '使用者滿意度',
explanation: '每 1000 條訊息的點贊數。反了使用者對回答十分滿意的比例。',
explanation: '每 1000 條訊息的點贊數。反了使用者對回答十分滿意的比例。',
},
avgResponseTime: {
title: '平均響應時間',

@ -1,4 +1,4 @@
import { useCallback } from 'react'
import { useCallback, useEffect } from 'react'
import type {
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
@ -39,6 +39,7 @@ import { useInvalidateAllBuiltInTools } from './use-tools'
import usePermission from '@/app/components/plugins/plugin-page/use-permission'
import { uninstallPlugin } from '@/service/plugins'
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
import { cloneDeep } from 'lodash-es'
const NAME_SPACE = 'plugins'
@ -383,6 +384,7 @@ export const usePluginTaskList = (category?: PluginType) => {
const {
data,
isFetched,
isRefetching,
refetch,
...rest
} = useQuery({
@ -392,16 +394,24 @@ export const usePluginTaskList = (category?: PluginType) => {
refetchInterval: (lastQuery) => {
const lastData = lastQuery.state.data
const taskDone = lastData?.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed)
return taskDone ? false : 5000
},
})
useEffect(() => {
// After first fetch, refresh plugin list each time all tasks are done
if (!isRefetching) {
const lastData = cloneDeep(data)
const taskDone = lastData?.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed)
const taskAllFailed = lastData?.tasks.every(task => task.status === TaskStatus.failed)
if (taskDone) {
if (lastData?.tasks.length && !taskAllFailed)
refreshPluginList(category ? { category } as any : undefined, !category)
return false
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isRefetching])
return 5000
},
})
const handleRefetch = useCallback(() => {
refetch()
}, [refetch])

Loading…
Cancel
Save