Merge branch 'main' into feat/rag-pipeline

This commit is contained in:
zxhlyh 2025-05-21 11:27:50 +08:00
commit e2585bc778
13 changed files with 88 additions and 93 deletions

View File

@ -754,7 +754,7 @@ class ProviderConfiguration(BaseModel):
:param only_active: return active model only
:return:
"""
provider_models = self.get_provider_models(model_type, only_active)
provider_models = self.get_provider_models(model_type, only_active, model)
for provider_model in provider_models:
if provider_model.model == model:
@ -763,12 +763,13 @@ class ProviderConfiguration(BaseModel):
return None
def get_provider_models(
self, model_type: Optional[ModelType] = None, only_active: bool = False
self, model_type: Optional[ModelType] = None, only_active: bool = False, model: Optional[str] = None
) -> list[ModelWithProviderEntity]:
"""
Get provider models.
:param model_type: model type
:param only_active: only active models
:param model: model name
:return:
"""
model_provider_factory = ModelProviderFactory(self.tenant_id)
@ -791,7 +792,10 @@ class ProviderConfiguration(BaseModel):
)
else:
provider_models = self._get_custom_provider_models(
model_types=model_types, provider_schema=provider_schema, model_setting_map=model_setting_map
model_types=model_types,
provider_schema=provider_schema,
model_setting_map=model_setting_map,
model=model,
)
if only_active:
@ -943,6 +947,7 @@ class ProviderConfiguration(BaseModel):
model_types: Sequence[ModelType],
provider_schema: ProviderEntity,
model_setting_map: dict[ModelType, dict[str, ModelSettings]],
model: Optional[str] = None,
) -> list[ModelWithProviderEntity]:
"""
Get custom provider models.
@ -995,7 +1000,8 @@ class ProviderConfiguration(BaseModel):
for model_configuration in self.custom_configuration.models:
if model_configuration.model_type not in model_types:
continue
if model and model != model_configuration.model:
continue
try:
custom_model_schema = self.get_model_schema(
model_type=model_configuration.model_type,

View File

@ -234,7 +234,11 @@ class OpsTraceManager:
return None
tracing_provider = app_ops_trace_config.get("tracing_provider")
if tracing_provider is None or tracing_provider not in provider_config_map:
if tracing_provider is None:
return None
try:
provider_config_map[tracing_provider]
except KeyError:
return None
# decrypt_token

View File

@ -190,7 +190,7 @@ class DatasetRetrieval:
retrieve_config.rerank_mode or "reranking_model",
retrieve_config.reranking_model,
retrieve_config.weights,
retrieve_config.reranking_enabled or True,
True if retrieve_config.reranking_enabled is None else retrieve_config.reranking_enabled,
message_id,
metadata_filter_document_ids,
metadata_condition,

View File

@ -11,6 +11,7 @@ from sqlalchemy import UnaryExpression, asc, delete, desc, select
from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker
from core.model_runtime.utils.encoders import jsonable_encoder
from core.workflow.entities.node_entities import NodeRunMetadataKey
from core.workflow.entities.node_execution_entities import (
NodeExecution,
@ -171,7 +172,9 @@ class SQLAlchemyWorkflowNodeExecutionRepository(WorkflowNodeExecutionRepository)
db_model.status = domain_model.status
db_model.error = domain_model.error
db_model.elapsed_time = domain_model.elapsed_time
db_model.execution_metadata = json.dumps(domain_model.metadata) if domain_model.metadata else None
db_model.execution_metadata = (
json.dumps(jsonable_encoder(domain_model.metadata)) if domain_model.metadata else None
)
db_model.created_at = domain_model.created_at
db_model.created_by_role = self._creator_user_role
db_model.created_by = self._creator_user_id

View File

@ -4,12 +4,14 @@ Unit tests for the SQLAlchemy implementation of WorkflowNodeExecutionRepository.
import json
from datetime import datetime
from decimal import Decimal
from unittest.mock import MagicMock, PropertyMock
import pytest
from pytest_mock import MockerFixture
from sqlalchemy.orm import Session, sessionmaker
from core.model_runtime.utils.encoders import jsonable_encoder
from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository
from core.workflow.entities.node_entities import NodeRunMetadataKey
from core.workflow.entities.node_execution_entities import NodeExecution, NodeExecutionStatus
@ -298,7 +300,7 @@ def test_to_db_model(repository):
status=NodeExecutionStatus.RUNNING,
error=None,
elapsed_time=1.5,
metadata={NodeRunMetadataKey.TOTAL_TOKENS: 100},
metadata={NodeRunMetadataKey.TOTAL_TOKENS: 100, NodeRunMetadataKey.TOTAL_PRICE: Decimal("0.0")},
created_at=datetime.now(),
finished_at=None,
)
@ -324,7 +326,7 @@ def test_to_db_model(repository):
assert db_model.inputs_dict == domain_model.inputs
assert db_model.process_data_dict == domain_model.process_data
assert db_model.outputs_dict == domain_model.outputs
assert db_model.execution_metadata_dict == domain_model.metadata
assert db_model.execution_metadata_dict == jsonable_encoder(domain_model.metadata)
assert db_model.status == domain_model.status
assert db_model.error == domain_model.error

View File

@ -31,126 +31,98 @@ type Props = {
appDetail: App
}
const Annotation: FC<Props> = ({
appDetail,
}) => {
const Annotation: FC<Props> = (props) => {
const { appDetail } = props
const { t } = useTranslation()
const [isShowEdit, setIsShowEdit] = React.useState(false)
const [isShowEdit, setIsShowEdit] = useState(false)
const [annotationConfig, setAnnotationConfig] = useState<AnnotationReplyConfig | null>(null)
const [isChatApp, setIsChatApp] = useState(false)
const [isChatApp] = useState(appDetail.mode !== 'completion')
const [controlRefreshSwitch, setControlRefreshSwitch] = useState(Date.now())
const { plan, enableBilling } = useProviderContext()
const isAnnotationFull = enableBilling && plan.usage.annotatedResponse >= plan.total.annotatedResponse
const [isShowAnnotationFullModal, setIsShowAnnotationFullModal] = useState(false)
const [queryParams, setQueryParams] = useState<QueryParam>({})
const [currPage, setCurrPage] = useState(0)
const [limit, setLimit] = useState(APP_PAGE_LIMIT)
const [list, setList] = useState<AnnotationItem[]>([])
const [total, setTotal] = useState(0)
const [isLoading, setIsLoading] = useState(false)
const [controlUpdateList, setControlUpdateList] = useState(Date.now())
const [currItem, setCurrItem] = useState<AnnotationItem | null>(null)
const [isShowViewModal, setIsShowViewModal] = useState(false)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
const fetchAnnotationConfig = async () => {
const res = await doFetchAnnotationConfig(appDetail.id)
setAnnotationConfig(res as AnnotationReplyConfig)
return (res as AnnotationReplyConfig).id
}
useEffect(() => {
const isChatApp = appDetail.mode !== 'completion'
setIsChatApp(isChatApp)
if (isChatApp)
fetchAnnotationConfig()
}, [])
const [controlRefreshSwitch, setControlRefreshSwitch] = useState(Date.now())
const { plan, enableBilling } = useProviderContext()
const isAnnotationFull = (enableBilling && plan.usage.annotatedResponse >= plan.total.annotatedResponse)
const [isShowAnnotationFullModal, setIsShowAnnotationFullModal] = useState(false)
const ensureJobCompleted = async (jobId: string, status: AnnotationEnableStatus) => {
let isCompleted = false
while (!isCompleted) {
const res: any = await queryAnnotationJobStatus(appDetail.id, status, jobId)
isCompleted = res.job_status === JobStatus.completed
if (isCompleted)
break
useEffect(() => {
if (isChatApp) fetchAnnotationConfig()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const ensureJobCompleted = async (jobId: string, status: AnnotationEnableStatus) => {
while (true) {
const res: any = await queryAnnotationJobStatus(appDetail.id, status, jobId)
if (res.job_status === JobStatus.completed) break
await sleep(2000)
}
}
const [queryParams, setQueryParams] = useState<QueryParam>({})
const [currPage, setCurrPage] = React.useState<number>(0)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
const [limit, setLimit] = React.useState<number>(APP_PAGE_LIMIT)
const query = {
page: currPage + 1,
limit,
keyword: debouncedQueryParams.keyword || '',
}
const [controlUpdateList, setControlUpdateList] = useState(Date.now())
const [list, setList] = useState<AnnotationItem[]>([])
const [total, setTotal] = useState(10)
const [isLoading, setIsLoading] = useState(false)
const fetchList = async (page = 1) => {
setIsLoading(true)
try {
const { data, total }: any = await fetchAnnotationList(appDetail.id, {
...query,
page,
limit,
keyword: debouncedQueryParams.keyword || '',
})
setList(data as AnnotationItem[])
setTotal(total)
}
catch {
finally {
setIsLoading(false)
}
setIsLoading(false)
}
useEffect(() => {
fetchList(currPage + 1)
}, [currPage])
useEffect(() => {
fetchList(1)
setControlUpdateList(Date.now())
}, [queryParams])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currPage, limit, debouncedQueryParams])
const handleAdd = async (payload: AnnotationItemBasic) => {
await addAnnotation(appDetail.id, {
...payload,
})
Toast.notify({
message: t('common.api.actionSuccess'),
type: 'success',
})
await addAnnotation(appDetail.id, payload)
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
const handleRemove = async (id: string) => {
await delAnnotation(appDetail.id, id)
Toast.notify({
message: t('common.api.actionSuccess'),
type: 'success',
})
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
const [currItem, setCurrItem] = useState<AnnotationItem | null>(list[0])
const [isShowViewModal, setIsShowViewModal] = useState(false)
useEffect(() => {
if (!isShowEdit)
setControlRefreshSwitch(Date.now())
}, [isShowEdit])
const handleView = (item: AnnotationItem) => {
setCurrItem(item)
setIsShowViewModal(true)
}
const handleSave = async (question: string, answer: string) => {
await editAnnotation(appDetail.id, (currItem as AnnotationItem).id, {
question,
answer,
})
Toast.notify({
message: t('common.api.actionSuccess'),
type: 'success',
})
if (!currItem) return
await editAnnotation(appDetail.id, currItem.id, { question, answer })
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
useEffect(() => {
if (!isShowEdit) setControlRefreshSwitch(Date.now())
}, [isShowEdit])
return (
<div className='flex h-full flex-col'>
<p className='system-sm-regular text-text-tertiary'>{t('appLog.description')}</p>
@ -211,6 +183,7 @@ const Annotation: FC<Props> = ({
</Filter>
{isLoading
? <Loading type='app' />
// eslint-disable-next-line sonarjs/no-nested-conditional
: total > 0
? <List
list={list}

View File

@ -18,7 +18,7 @@ import style from './style.module.css'
import type { ConfigParams } from './settings'
import Tooltip from '@/app/components/base/tooltip'
import AppBasic from '@/app/components/app-sidebar/basic'
import { asyncRunSafe, randomString } from '@/utils'
import { asyncRunSafe } from '@/utils'
import { basePath } from '@/utils/var'
import { useStore as useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
@ -184,7 +184,7 @@ function AppCard({
: t('appOverview.overview.apiInfo.explanation')
}
/>
<div className='flex items-center gap-1'>
<div className='flex shrink-0 items-center gap-1'>
<Indicator color={runningStatus ? 'green' : 'yellow'} />
<div className={`${runningStatus ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
{runningStatus
@ -210,7 +210,7 @@ function AppCard({
content={isApp ? appUrl : apiUrl}
className={'!size-6'}
/>
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} className='z-50 !size-6 rounded-md hover:bg-state-base-hover' selectorId={randomString(8)} />}
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} />}
{isApp && <Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />}
{/* button copy link/ button regenerate */}
{showConfirmDelete && (

View File

@ -94,7 +94,7 @@ const ImageInput: FC<UploaderProps> = ({
<div
className={classNames(
isDragActive && 'border-primary-600',
'relative aspect-square bg-gray-50 border-[1.5px] border-gray-200 border-dashed rounded-lg flex flex-col justify-center items-center text-gray-500')}
'relative aspect-square border-[1.5px] border-dashed rounded-lg flex flex-col justify-center items-center text-gray-500')}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}

View File

@ -115,7 +115,7 @@ const AppIconPicker: FC<AppIconPickerProps> = ({
className={cn(s.container, '!w-[362px] !p-0')}
>
{!DISABLE_UPLOAD_IMAGE_AS_ICON && <div className="w-full p-2 pb-0">
<div className='flex items-center justify-center gap-2 rounded-xl bg-background-body p-1'>
<div className='flex items-center justify-center gap-2 rounded-xl bg-background-body p-1 text-text-primary'>
{tabs.map(tab => (
<button
key={tab.key}

View File

@ -4,9 +4,6 @@
align-items: flex-start;
width: 362px;
max-height: 552px;
border: 0.5px solid #EAECF0;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
border-radius: 12px;
background: #fff;
}

View File

@ -216,16 +216,24 @@ CodeBlock.displayName = 'CodeBlock'
const VideoBlock: any = memo(({ node }: any) => {
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
if (srcs.length === 0)
if (srcs.length === 0) {
const src = node.properties?.src
if (src)
return <VideoGallery key={src} srcs={[src]} />
return null
}
return <VideoGallery key={srcs.join()} srcs={srcs} />
})
VideoBlock.displayName = 'VideoBlock'
const AudioBlock: any = memo(({ node }: any) => {
const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
if (srcs.length === 0)
if (srcs.length === 0) {
const src = node.properties?.src
if (src)
return <AudioGallery key={src} srcs={[src]} />
return null
}
return <AudioGallery key={srcs.join()} srcs={srcs} />
})
AudioBlock.displayName = 'AudioBlock'

View File

@ -49,8 +49,8 @@ const PluginSettingModal: FC<Props> = ({
</div>
<div className='flex flex-col items-start justify-center gap-4 self-stretch px-6 py-3'>
{[
{ title: t(`${i18nPrefix}.whoCanInstall`), key: 'install_permission', value: tempPrivilege.install_permission },
{ title: t(`${i18nPrefix}.whoCanDebug`), key: 'debug_permission', value: tempPrivilege.debug_permission },
{ title: t(`${i18nPrefix}.whoCanInstall`), key: 'install_permission', value: tempPrivilege?.install_permission || PermissionType.noOne },
{ title: t(`${i18nPrefix}.whoCanDebug`), key: 'debug_permission', value: tempPrivilege?.debug_permission || PermissionType.noOne },
].map(({ title, key, value }) => (
<div key={key} className='flex flex-col items-start gap-1 self-stretch'>
<div className='flex h-6 items-center gap-0.5'>

View File

@ -21,4 +21,6 @@
z-index: -1000 !important;
}
#workflow-container .react-flow {}
#workflow-container .react-flow__attribution {
background: none !important;
}