diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index 81d582fb9a..66d8d0f414 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -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, diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index 8ddeb1b846..84520a5991 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -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 diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index d3605da146..c4adf6de4d 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -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, diff --git a/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py b/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py index 3bf775db13..7e0115e4e7 100644 --- a/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py +++ b/api/core/repositories/sqlalchemy_workflow_node_execution_repository.py @@ -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 diff --git a/api/tests/unit_tests/repositories/workflow_node_execution/test_sqlalchemy_repository.py b/api/tests/unit_tests/repositories/workflow_node_execution/test_sqlalchemy_repository.py index 8c10b272fb..7c5020db02 100644 --- a/api/tests/unit_tests/repositories/workflow_node_execution/test_sqlalchemy_repository.py +++ b/api/tests/unit_tests/repositories/workflow_node_execution/test_sqlalchemy_repository.py @@ -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 diff --git a/web/app/components/app/annotation/index.tsx b/web/app/components/app/annotation/index.tsx index f010f5f8b1..04bce1947b 100644 --- a/web/app/components/app/annotation/index.tsx +++ b/web/app/components/app/annotation/index.tsx @@ -31,126 +31,98 @@ type Props = { appDetail: App } -const Annotation: FC = ({ - appDetail, -}) => { +const Annotation: FC = (props) => { + const { appDetail } = props const { t } = useTranslation() - const [isShowEdit, setIsShowEdit] = React.useState(false) + const [isShowEdit, setIsShowEdit] = useState(false) const [annotationConfig, setAnnotationConfig] = useState(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({}) + const [currPage, setCurrPage] = useState(0) + const [limit, setLimit] = useState(APP_PAGE_LIMIT) + const [list, setList] = useState([]) + const [total, setTotal] = useState(0) + const [isLoading, setIsLoading] = useState(false) + const [controlUpdateList, setControlUpdateList] = useState(Date.now()) + const [currItem, setCurrItem] = useState(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({}) - const [currPage, setCurrPage] = React.useState(0) - const debouncedQueryParams = useDebounce(queryParams, { wait: 500 }) - const [limit, setLimit] = React.useState(APP_PAGE_LIMIT) - const query = { - page: currPage + 1, - limit, - keyword: debouncedQueryParams.keyword || '', - } - - const [controlUpdateList, setControlUpdateList] = useState(Date.now()) - const [list, setList] = useState([]) - 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(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 (

{t('appLog.description')}

@@ -211,6 +183,7 @@ const Annotation: FC = ({ {isLoading ? + // eslint-disable-next-line sonarjs/no-nested-conditional : total > 0 ? -
+
{runningStatus @@ -210,7 +210,7 @@ function AppCard({ content={isApp ? appUrl : apiUrl} className={'!size-6'} /> - {isApp && } + {isApp && } {isApp && } {/* button copy link/ button regenerate */} {showConfirmDelete && ( diff --git a/web/app/components/base/app-icon-picker/ImageInput.tsx b/web/app/components/base/app-icon-picker/ImageInput.tsx index 9c0a95c021..d42abf867f 100644 --- a/web/app/components/base/app-icon-picker/ImageInput.tsx +++ b/web/app/components/base/app-icon-picker/ImageInput.tsx @@ -94,7 +94,7 @@ const ImageInput: FC = ({
= ({ className={cn(s.container, '!w-[362px] !p-0')} > {!DISABLE_UPLOAD_IMAGE_AS_ICON &&
-
+
{tabs.map(tab => (
{[ - { 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 }) => (
diff --git a/web/app/components/workflow/style.css b/web/app/components/workflow/style.css index 7ef46dd5bf..9d88ac2644 100644 --- a/web/app/components/workflow/style.css +++ b/web/app/components/workflow/style.css @@ -21,4 +21,6 @@ z-index: -1000 !important; } -#workflow-container .react-flow {} +#workflow-container .react-flow__attribution { + background: none !important; +}