diff --git a/api/controllers/console/app/app.py b/api/controllers/console/app/app.py index 58ed6efc14..76f786f13b 100644 --- a/api/controllers/console/app/app.py +++ b/api/controllers/console/app/app.py @@ -38,7 +38,7 @@ from libs.login import current_account_with_tenant, login_required from models import App, DatasetPermissionEnum, Workflow from models.model import IconType from services.app_dsl_service import AppDslService -from services.app_service import AppService +from services.app_service import AppListParams, AppService, CreateAppParams from services.enterprise.enterprise_service import EnterpriseService from services.entities.dsl_entities import ImportMode, ImportStatus from services.entities.knowledge_entities.knowledge_entities import ( @@ -477,11 +477,18 @@ class AppListApi(Resource): current_user, current_tenant_id = current_account_with_tenant() args = AppListQuery.model_validate(_normalize_app_list_query_args(request.args)) - args_dict = args.model_dump() + params = AppListParams( + page=args.page, + limit=args.limit, + mode=args.mode, + name=args.name, + tag_ids=args.tag_ids, + is_created_by_me=args.is_created_by_me, + ) # get app list app_service = AppService() - app_pagination = app_service.get_paginate_apps(current_user.id, current_tenant_id, args_dict) + app_pagination = app_service.get_paginate_apps(current_user.id, current_tenant_id, params) if not app_pagination: empty = AppPagination(page=args.page, limit=args.limit, total=0, has_more=False, data=[]) return empty.model_dump(mode="json"), 200 @@ -545,9 +552,17 @@ class AppListApi(Resource): """Create app""" current_user, current_tenant_id = current_account_with_tenant() args = CreateAppPayload.model_validate(console_ns.payload) + params = CreateAppParams( + name=args.name, + description=args.description, + mode=args.mode, + icon_type=args.icon_type, + icon=args.icon, + icon_background=args.icon_background, + ) app_service = AppService() - app = app_service.create_app(current_tenant_id, args.model_dump(), current_user) + app = app_service.create_app(current_tenant_id, params, current_user) app_detail = AppDetail.model_validate(app, from_attributes=True) return app_detail.model_dump(mode="json"), 201 diff --git a/api/services/app_service.py b/api/services/app_service.py index a046b909b3..6716833f6c 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -1,9 +1,10 @@ import json import logging -from typing import Any, TypedDict, cast +from typing import Any, Literal, TypedDict, cast import sqlalchemy as sa from flask_sqlalchemy.pagination import Pagination +from pydantic import BaseModel, Field from sqlalchemy import select from configs import dify_config @@ -31,39 +32,59 @@ from tasks.remove_app_and_related_data_task import remove_app_and_related_data_t logger = logging.getLogger(__name__) +class AppListParams(BaseModel): + page: int = Field(default=1, ge=1) + limit: int = Field(default=20, ge=1, le=100) + mode: Literal["completion", "chat", "advanced-chat", "workflow", "agent-chat", "channel", "all"] = "all" + name: str | None = None + tag_ids: list[str] | None = None + is_created_by_me: bool | None = None + + +class CreateAppParams(BaseModel): + name: str = Field(min_length=1) + description: str | None = None + mode: Literal["chat", "agent-chat", "advanced-chat", "workflow", "completion"] + icon_type: str | None = None + icon: str | None = None + icon_background: str | None = None + api_rph: int = 0 + api_rpm: int = 0 + max_active_requests: int | None = None + + class AppService: - def get_paginate_apps(self, user_id: str, tenant_id: str, args: dict[str, Any]) -> Pagination | None: + def get_paginate_apps(self, user_id: str, tenant_id: str, params: AppListParams) -> Pagination | None: """ Get app list with pagination :param user_id: user id :param tenant_id: tenant id - :param args: request args + :param params: query parameters :return: """ filters = [App.tenant_id == tenant_id, App.is_universal == False] - if args["mode"] == "workflow": + if params.mode == "workflow": filters.append(App.mode == AppMode.WORKFLOW) - elif args["mode"] == "completion": + elif params.mode == "completion": filters.append(App.mode == AppMode.COMPLETION) - elif args["mode"] == "chat": + elif params.mode == "chat": filters.append(App.mode == AppMode.CHAT) - elif args["mode"] == "advanced-chat": + elif params.mode == "advanced-chat": filters.append(App.mode == AppMode.ADVANCED_CHAT) - elif args["mode"] == "agent-chat": + elif params.mode == "agent-chat": filters.append(App.mode == AppMode.AGENT_CHAT) - if args.get("is_created_by_me", False): + if params.is_created_by_me: filters.append(App.created_by == user_id) - if args.get("name"): + if params.name: from libs.helper import escape_like_pattern - name = args["name"][:30] + name = params.name[:30] escaped_name = escape_like_pattern(name) filters.append(App.name.ilike(f"%{escaped_name}%", escape="\\")) - # Check if tag_ids is not empty to avoid WHERE false condition - if args.get("tag_ids") and len(args["tag_ids"]) > 0: - target_ids = TagService.get_target_ids_by_tag_ids("app", tenant_id, args["tag_ids"]) + if params.tag_ids and len(params.tag_ids) > 0: + target_ids = TagService.get_target_ids_by_tag_ids("app", tenant_id, params.tag_ids) if target_ids and len(target_ids) > 0: filters.append(App.id.in_(target_ids)) else: @@ -71,21 +92,21 @@ class AppService: app_models = db.paginate( sa.select(App).where(*filters).order_by(App.created_at.desc()), - page=args["page"], - per_page=args["limit"], + page=params.page, + per_page=params.limit, error_out=False, ) return app_models - def create_app(self, tenant_id: str, args: dict[str, Any], account: Account) -> App: + def create_app(self, tenant_id: str, params: CreateAppParams, account: Account) -> App: """ Create app :param tenant_id: tenant id - :param args: request args + :param params: app creation parameters :param account: Account instance """ - app_mode = AppMode.value_of(args["mode"]) + app_mode = AppMode.value_of(params.mode) app_template = default_app_templates[app_mode] # get model config @@ -143,15 +164,16 @@ class AppService: default_model_config["model"] = json.dumps(default_model_dict) app = App(**app_template["app"]) - app.name = args["name"] - app.description = args.get("description", "") - app.mode = args["mode"] - app.icon_type = args.get("icon_type", "emoji") - app.icon = args["icon"] - app.icon_background = args["icon_background"] + app.name = params.name + app.description = params.description or "" + app.mode = app_mode + app.icon_type = IconType(params.icon_type) if params.icon_type else IconType.EMOJI + app.icon = params.icon + app.icon_background = params.icon_background app.tenant_id = tenant_id - app.api_rph = args.get("api_rph", 0) - app.api_rpm = args.get("api_rpm", 0) + app.api_rph = params.api_rph + app.api_rpm = params.api_rpm + app.max_active_requests = params.max_active_requests app.created_by = account.id app.updated_by = account.id diff --git a/api/tests/test_containers_integration_tests/services/test_agent_service.py b/api/tests/test_containers_integration_tests/services/test_agent_service.py index 00a2f9a59f..74cda54ea2 100644 --- a/api/tests/test_containers_integration_tests/services/test_agent_service.py +++ b/api/tests/test_containers_integration_tests/services/test_agent_service.py @@ -11,7 +11,7 @@ from models.enums import ConversationFromSource, MessageFileBelongsTo from models.model import AppModelConfig, Conversation, EndUser, Message, MessageAgentThought from services.account_service import AccountService, TenantService from services.agent_service import AgentService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -119,16 +119,16 @@ class TestAgentService: tenant = account.current_tenant # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "agent-chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="agent-chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) diff --git a/api/tests/test_containers_integration_tests/services/test_annotation_service.py b/api/tests/test_containers_integration_tests/services/test_annotation_service.py index 95fc73f45a..bc75562d15 100644 --- a/api/tests/test_containers_integration_tests/services/test_annotation_service.py +++ b/api/tests/test_containers_integration_tests/services/test_annotation_service.py @@ -9,7 +9,7 @@ from models import Account from models.enums import ConversationFromSource, InvokeFrom from models.model import MessageAnnotation from services.annotation_service import AppAnnotationService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -86,16 +86,16 @@ class TestAnnotationService: tenant = account.current_tenant # Setup app creation arguments - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) # Create app app_service = AppService() diff --git a/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py b/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py index 7c5d2390ba..bfde1f1301 100644 --- a/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py +++ b/api/tests/test_containers_integration_tests/services/test_app_dsl_service.py @@ -37,7 +37,7 @@ from services.app_dsl_service import ( PendingData, _check_version_compatibility, ) -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from tests.test_containers_integration_tests.helpers import generate_valid_password _DEFAULT_TENANT_ID = "00000000-0000-0000-0000-000000000001" @@ -147,16 +147,16 @@ class TestAppDslService: ) TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) tenant = account.current_tenant - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) return app, account diff --git a/api/tests/test_containers_integration_tests/services/test_app_generate_service.py b/api/tests/test_containers_integration_tests/services/test_app_generate_service.py index e2fe6c8476..8be4c040b7 100644 --- a/api/tests/test_containers_integration_tests/services/test_app_generate_service.py +++ b/api/tests/test_containers_integration_tests/services/test_app_generate_service.py @@ -1,4 +1,5 @@ import uuid +from typing import Literal from unittest.mock import ANY, MagicMock, patch import pytest @@ -133,7 +134,10 @@ class TestAppGenerateService: } def _create_test_app_and_account( - self, db_session_with_containers: Session, mock_external_service_dependencies, mode="chat" + self, + db_session_with_containers: Session, + mock_external_service_dependencies, + mode: Literal["chat", "agent-chat", "advanced-chat", "workflow", "completion"] = "chat", ): """ Helper method to create a test app and account for testing. @@ -165,20 +169,20 @@ class TestAppGenerateService: TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) tenant = account.current_tenant - # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": mode, - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - "max_active_requests": 5, - } + from services.app_service import AppService, CreateAppParams - from services.app_service import AppService + # Create app with realistic data + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode=mode, + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + max_active_requests=5, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) diff --git a/api/tests/test_containers_integration_tests/services/test_app_service.py b/api/tests/test_containers_integration_tests/services/test_app_service.py index b695ae9fd9..87e355a6df 100644 --- a/api/tests/test_containers_integration_tests/services/test_app_service.py +++ b/api/tests/test_containers_integration_tests/services/test_app_service.py @@ -2,6 +2,7 @@ from unittest.mock import create_autospec, patch import pytest from faker import Faker +from pydantic import ValidationError from sqlalchemy.orm import Session from constants.model_template import default_app_templates @@ -11,7 +12,7 @@ from services.account_service import AccountService, TenantService from tests.test_containers_integration_tests.helpers import generate_valid_password # Delay import of AppService to avoid circular dependency -# from services.app_service import AppService +# from services.app_service import AppService, AppListParams, CreateAppParams class TestAppService: @@ -63,34 +64,34 @@ class TestAppService: tenant = account.current_tenant # Setup app creation arguments - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + # Import here to avoid circular dependency + from services.app_service import AppService, CreateAppParams + + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) # Create app - # Import here to avoid circular dependency - from services.app_service import AppService - app_service = AppService() - app = app_service.create_app(tenant.id, app_args, account) + app = app_service.create_app(tenant.id, app_params, account) # Verify app was created correctly - assert app.name == app_args["name"] - assert app.description == app_args["description"] - assert app.mode == app_args["mode"] - assert app.icon_type == app_args["icon_type"] - assert app.icon == app_args["icon"] - assert app.icon_background == app_args["icon_background"] + assert app.name == app_params.name + assert app.description == app_params.description + assert app.mode == app_params.mode + assert app.icon_type == app_params.icon_type + assert app.icon == app_params.icon + assert app.icon_background == app_params.icon_background assert app.tenant_id == tenant.id - assert app.api_rph == app_args["api_rph"] - assert app.api_rpm == app_args["api_rpm"] + assert app.api_rph == app_params.api_rph + assert app.api_rpm == app_params.api_rpm assert app.created_by == account.id assert app.updated_by == account.id assert app.status == "normal" @@ -119,7 +120,7 @@ class TestAppService: tenant = account.current_tenant # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams app_service = AppService() @@ -128,20 +129,20 @@ class TestAppService: app_modes = [v.value for v in default_app_templates] for mode in app_modes: - app_args = { - "name": f"{fake.company()} {mode}", - "description": f"Test app for {mode} mode", - "mode": mode, - "icon_type": "emoji", - "icon": "🚀", - "icon_background": "#4ECDC4", - } + app_params = CreateAppParams( + name=f"{fake.company()} {mode}", + description=f"Test app for {mode} mode", + mode=mode, + icon_type="emoji", + icon="🚀", + icon_background="#4ECDC4", + ) - app = app_service.create_app(tenant.id, app_args, account) + app = app_service.create_app(tenant.id, app_params, account) # Verify app mode was set correctly assert app.mode == mode - assert app.name == app_args["name"] + assert app.name == app_params.name assert app.tenant_id == tenant.id assert app.created_by == account.id @@ -162,20 +163,20 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ) app_service = AppService() - created_app = app_service.create_app(tenant.id, app_args, account) + created_app = app_service.create_app(tenant.id, app_params, account) # Get app using the service - needs current_user mock mock_current_user = create_autospec(Account, instance=True) @@ -210,31 +211,27 @@ class TestAppService: tenant = account.current_tenant # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppListParams, AppService, CreateAppParams app_service = AppService() # Create multiple apps app_names = [fake.company() for _ in range(5)] for name in app_names: - app_args = { - "name": name, - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "📱", - "icon_background": "#96CEB4", - } - app_service.create_app(tenant.id, app_args, account) + app_params = CreateAppParams( + name=name, + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="📱", + icon_background="#96CEB4", + ) + app_service.create_app(tenant.id, app_params, account) # Get paginated apps - args = { - "page": 1, - "limit": 10, - "mode": "chat", - } + params = AppListParams(page=1, limit=10, mode="chat") - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, params) # Verify pagination results assert paginated_apps is not None @@ -266,60 +263,47 @@ class TestAppService: tenant = account.current_tenant # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppListParams, AppService, CreateAppParams app_service = AppService() # Create apps with different modes - chat_app_args = { - "name": "Chat App", - "description": "A chat application", - "mode": "chat", - "icon_type": "emoji", - "icon": "💬", - "icon_background": "#FF6B6B", - } - completion_app_args = { - "name": "Completion App", - "description": "A completion application", - "mode": "completion", - "icon_type": "emoji", - "icon": "✍️", - "icon_background": "#4ECDC4", - } + chat_app_params = CreateAppParams( + name="Chat App", + description="A chat application", + mode="chat", + icon_type="emoji", + icon="💬", + icon_background="#FF6B6B", + ) + completion_app_params = CreateAppParams( + name="Completion App", + description="A completion application", + mode="completion", + icon_type="emoji", + icon="✍️", + icon_background="#4ECDC4", + ) - chat_app = app_service.create_app(tenant.id, chat_app_args, account) - completion_app = app_service.create_app(tenant.id, completion_app_args, account) + chat_app = app_service.create_app(tenant.id, chat_app_params, account) + completion_app = app_service.create_app(tenant.id, completion_app_params, account) # Test filter by mode - chat_args = { - "page": 1, - "limit": 10, - "mode": "chat", - } - chat_apps = app_service.get_paginate_apps(account.id, tenant.id, chat_args) + chat_apps = app_service.get_paginate_apps(account.id, tenant.id, AppListParams(page=1, limit=10, mode="chat")) assert len(chat_apps.items) == 1 assert chat_apps.items[0].mode == "chat" # Test filter by name - name_args = { - "page": 1, - "limit": 10, - "mode": "chat", - "name": "Chat", - } - filtered_apps = app_service.get_paginate_apps(account.id, tenant.id, name_args) + filtered_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(page=1, limit=10, mode="chat", name="Chat") + ) assert len(filtered_apps.items) == 1 assert "Chat" in filtered_apps.items[0].name # Test filter by created_by_me - created_by_me_args = { - "page": 1, - "limit": 10, - "mode": "completion", - "is_created_by_me": True, - } - my_apps = app_service.get_paginate_apps(account.id, tenant.id, created_by_me_args) + my_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(page=1, limit=10, mode="completion", is_created_by_me=True) + ) assert len(my_apps.items) == 1 def test_get_paginate_apps_with_tag_filters( @@ -341,34 +325,29 @@ class TestAppService: tenant = account.current_tenant # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppListParams, AppService, CreateAppParams app_service = AppService() # Create an app - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🏷️", - "icon_background": "#FFEAA7", - } - app = app_service.create_app(tenant.id, app_args, account) + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🏷️", + icon_background="#FFEAA7", + ) + app = app_service.create_app(tenant.id, app_params, account) # Mock TagService to return the app ID for tag filtering with patch("services.app_service.TagService.get_target_ids_by_tag_ids") as mock_tag_service: mock_tag_service.return_value = [app.id] # Test with tag filter - args = { - "page": 1, - "limit": 10, - "mode": "chat", - "tag_ids": ["tag1", "tag2"], - } + params = AppListParams(page=1, limit=10, mode="chat", tag_ids=["tag1", "tag2"]) - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, params) # Verify tag service was called mock_tag_service.assert_called_once_with("app", tenant.id, ["tag1", "tag2"]) @@ -382,14 +361,9 @@ class TestAppService: with patch("services.app_service.TagService.get_target_ids_by_tag_ids") as mock_tag_service: mock_tag_service.return_value = [] - args = { - "page": 1, - "limit": 10, - "mode": "chat", - "tag_ids": ["nonexistent_tag"], - } + params = AppListParams(page=1, limit=10, mode="chat", tag_ids=["nonexistent_tag"]) - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, params) # Should return None when no apps match tag filter assert paginated_apps is None @@ -411,20 +385,20 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ) app_service = AppService() - app = app_service.create_app(tenant.id, app_args, account) + app = app_service.create_app(tenant.id, app_params, account) # Store original values original_name = app.name @@ -480,19 +454,19 @@ class TestAppService: TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) tenant = account.current_tenant - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams app_service = AppService() app = app_service.create_app( tenant.id, - { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - }, + CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ), account, ) @@ -532,19 +506,19 @@ class TestAppService: TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) tenant = account.current_tenant - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams app_service = AppService() app = app_service.create_app( tenant.id, - { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - }, + CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ), account, ) @@ -583,20 +557,20 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ) app_service = AppService() - app = app_service.create_app(tenant.id, app_args, account) + app = app_service.create_app(tenant.id, app_params, account) # Store original name original_name = app.name @@ -636,20 +610,20 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🎯", - "icon_background": "#45B7D1", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + app_params = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🎯", + icon_background="#45B7D1", + ) app_service = AppService() - app = app_service.create_app(tenant.id, app_args, account) + app = app_service.create_app(tenant.id, app_params, account) # Store original values original_icon = app.icon @@ -697,18 +671,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🌐", - "icon_background": "#74B9FF", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🌐", + icon_background="#74B9FF", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -757,18 +730,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🔌", - "icon_background": "#A29BFE", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🔌", + icon_background="#A29BFE", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -817,18 +789,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🔄", - "icon_background": "#FD79A8", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🔄", + icon_background="#FD79A8", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -868,18 +839,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🗑️", - "icon_background": "#E17055", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🗑️", + icon_background="#E17055", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -920,18 +890,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🧹", - "icon_background": "#00B894", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🧹", + icon_background="#00B894", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -980,18 +949,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "📊", - "icon_background": "#6C5CE7", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="📊", + icon_background="#6C5CE7", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -1019,18 +987,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🔗", - "icon_background": "#FDCB6E", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🔗", + icon_background="#FDCB6E", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -1059,18 +1026,17 @@ class TestAppService: tenant = account.current_tenant # Create app first - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🆔", - "icon_background": "#E84393", - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🆔", + icon_background="#E84393", + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -1106,26 +1072,20 @@ class TestAppService: password=generate_valid_password(fake), ) TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) - tenant = account.current_tenant - - # Setup app creation arguments with invalid mode - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "invalid_mode", # Invalid mode - "icon_type": "emoji", - "icon": "❌", - "icon_background": "#D63031", - } # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import CreateAppParams - app_service = AppService() - - # Attempt to create app with invalid mode - with pytest.raises(ValueError, match="invalid mode value"): - app_service.create_app(tenant.id, app_args, account) + # Attempt to create app with invalid mode - Pydantic will reject invalid literal + with pytest.raises(ValidationError): + CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="invalid_mode", # type: ignore[arg-type] + icon_type="emoji", + icon="❌", + icon_background="#D63031", + ) def test_get_apps_with_special_characters_in_name( self, db_session_with_containers: Session, mock_external_service_dependencies @@ -1151,99 +1111,103 @@ class TestAppService: tenant = account.current_tenant # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppListParams, AppService, CreateAppParams app_service = AppService() # Create apps with special characters in names app_with_percent = app_service.create_app( tenant.id, - { - "name": "App with 50% discount", - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - }, + CreateAppParams( + name="App with 50% discount", + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ), account, ) app_with_underscore = app_service.create_app( tenant.id, - { - "name": "test_data_app", - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - }, + CreateAppParams( + name="test_data_app", + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ), account, ) app_with_backslash = app_service.create_app( tenant.id, - { - "name": "path\\to\\app", - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - }, + CreateAppParams( + name="path\\to\\app", + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ), account, ) # Create app that should NOT match app_no_match = app_service.create_app( tenant.id, - { - "name": "100% different", - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - }, + CreateAppParams( + name="100% different", + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ), account, ) # Test 1: Search with % character - args = {"name": "50%", "mode": "chat", "page": 1, "limit": 10} - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(name="50%", mode="chat", page=1, limit=10) + ) assert paginated_apps is not None assert paginated_apps.total == 1 assert len(paginated_apps.items) == 1 assert paginated_apps.items[0].name == "App with 50% discount" # Test 2: Search with _ character - args = {"name": "test_data", "mode": "chat", "page": 1, "limit": 10} - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(name="test_data", mode="chat", page=1, limit=10) + ) assert paginated_apps is not None assert paginated_apps.total == 1 assert len(paginated_apps.items) == 1 assert paginated_apps.items[0].name == "test_data_app" # Test 3: Search with \ character - args = {"name": "path\\to\\app", "mode": "chat", "page": 1, "limit": 10} - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(name="path\\to\\app", mode="chat", page=1, limit=10) + ) assert paginated_apps is not None assert paginated_apps.total == 1 assert len(paginated_apps.items) == 1 assert paginated_apps.items[0].name == "path\\to\\app" # Test 4: Search with % should NOT match 100% (verifies escaping works) - args = {"name": "50%", "mode": "chat", "page": 1, "limit": 10} - paginated_apps = app_service.get_paginate_apps(account.id, tenant.id, args) + paginated_apps = app_service.get_paginate_apps( + account.id, tenant.id, AppListParams(name="50%", mode="chat", page=1, limit=10) + ) assert paginated_apps is not None assert paginated_apps.total == 1 assert all("50%" in app.name for app in paginated_apps.items) @@ -1254,7 +1218,7 @@ class TestAppService: """Test get_app_code_by_id raises ValueError when site is missing.""" from uuid import uuid4 - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams with pytest.raises(ValueError, match="not found"): AppService.get_app_code_by_id(str(uuid4())) @@ -1263,7 +1227,7 @@ class TestAppService: self, db_session_with_containers: Session, mock_external_service_dependencies ): """Test get_app_id_by_code raises ValueError when code does not exist.""" - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams with pytest.raises(ValueError, match="not found"): AppService.get_app_id_by_code("nonexistent-code") @@ -1274,7 +1238,7 @@ class TestAppService: """Test get_app_meta returns empty tool_icons when workflow is None.""" from types import SimpleNamespace - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams app_service = AppService() workflow_app = SimpleNamespace(mode="workflow", workflow=None) @@ -1288,7 +1252,7 @@ class TestAppService: """Test get_app_meta returns empty tool_icons when app_model_config is None.""" from types import SimpleNamespace - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams app_service = AppService() chat_app = SimpleNamespace(mode="chat", app_model_config=None) diff --git a/api/tests/test_containers_integration_tests/services/test_message_service.py b/api/tests/test_containers_integration_tests/services/test_message_service.py index bdf6d9b951..6d0d281c6b 100644 --- a/api/tests/test_containers_integration_tests/services/test_message_service.py +++ b/api/tests/test_containers_integration_tests/services/test_message_service.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import Session from models.enums import ConversationFromSource, FeedbackRating, InvokeFrom from models.model import MessageFeedback -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.errors.message import ( FirstMessageNotExistsError, LastMessageNotExistsError, @@ -103,16 +103,16 @@ class TestMessageService: tenant = account.current_tenant # Setup app creation arguments - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "advanced-chat", # Use advanced-chat mode to use mocked workflow - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="advanced-chat", # Use advanced-chat mode to use mocked workflow, + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) # Create app app_service = AppService() diff --git a/api/tests/test_containers_integration_tests/services/test_ops_service.py b/api/tests/test_containers_integration_tests/services/test_ops_service.py index e2e1a228b2..ff76bce416 100644 --- a/api/tests/test_containers_integration_tests/services/test_ops_service.py +++ b/api/tests/test_containers_integration_tests/services/test_ops_service.py @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session from core.ops.entities.config_entity import TracingProviderEnum from models.model import TraceAppConfig from services.account_service import AccountService, TenantService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.ops_service import OpsService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -57,14 +57,14 @@ class TestOpsService: app_service = AppService() app = app_service.create_app( tenant.id, - { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - }, + CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + ), account, ) return app, account diff --git a/api/tests/test_containers_integration_tests/services/test_saved_message_service.py b/api/tests/test_containers_integration_tests/services/test_saved_message_service.py index 7b9e9924cd..7368ad4249 100644 --- a/api/tests/test_containers_integration_tests/services/test_saved_message_service.py +++ b/api/tests/test_containers_integration_tests/services/test_saved_message_service.py @@ -8,7 +8,7 @@ from models import App, CreatorUserRole from models.enums import ConversationFromSource from models.model import EndUser, Message from models.web import SavedMessage -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.saved_message_service import SavedMessageService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -73,16 +73,16 @@ class TestSavedMessageService: tenant = account.current_tenant # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) diff --git a/api/tests/test_containers_integration_tests/services/test_web_conversation_service.py b/api/tests/test_containers_integration_tests/services/test_web_conversation_service.py index 797731d04b..8e53a2d6cd 100644 --- a/api/tests/test_containers_integration_tests/services/test_web_conversation_service.py +++ b/api/tests/test_containers_integration_tests/services/test_web_conversation_service.py @@ -11,7 +11,7 @@ from models.enums import ConversationFromSource from models.model import Conversation, EndUser from models.web import PinnedConversation from services.account_service import AccountService, TenantService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.web_conversation_service import WebConversationService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -77,16 +77,16 @@ class TestWebConversationService: tenant = account.current_tenant # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py index a2cdddad61..07a49130d0 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_app_service.py @@ -17,7 +17,7 @@ from models.workflow import WorkflowAppLogCreatedFrom from services.account_service import AccountService, TenantService # Delay import of AppService to avoid circular dependency -# from services.app_service import AppService +# from services.app_service import AppService, CreateAppParams from services.workflow_app_service import LogView, WorkflowAppService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -82,20 +82,20 @@ class TestWorkflowAppService: TenantService.create_owner_tenant_if_not_exist(account, name=fake.company()) tenant = account.current_tenant - # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "workflow", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + # Create app with realistic data + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="workflow", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -146,20 +146,20 @@ class TestWorkflowAppService: """ fake = Faker() - # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "workflow", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } - # Import here to avoid circular dependency - from services.app_service import AppService + from services.app_service import AppService, CreateAppParams + + # Create app with realistic data + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="workflow", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_run_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_run_service.py index d02a078281..09fe1570bc 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_run_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_run_service.py @@ -13,7 +13,7 @@ from models.model import ( ) from models.workflow import WorkflowRun from services.account_service import AccountService, TenantService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.workflow_run_service import WorkflowRunService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -79,16 +79,16 @@ class TestWorkflowRunService: tenant = account.current_tenant # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "chat", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="chat", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account) @@ -535,13 +535,13 @@ class TestWorkflowRunService: tenant = account.current_tenant # Create app - app_args = { - "name": "Test App", - "mode": "chat", - "icon_type": "emoji", - "icon": "🚀", - "icon_background": "#4ECDC4", - } + app_args = CreateAppParams( + name="Test App", + mode="chat", + icon_type="emoji", + icon="🚀", + icon_background="#4ECDC4", + ) app = app_service.create_app(tenant.id, app_args, account) # Create workflow run without node executions @@ -586,13 +586,13 @@ class TestWorkflowRunService: tenant = account.current_tenant # Create app - app_args = { - "name": "Test App", - "mode": "chat", - "icon_type": "emoji", - "icon": "🚀", - "icon_background": "#4ECDC4", - } + app_args = CreateAppParams( + name="Test App", + mode="chat", + icon_type="emoji", + icon="🚀", + icon_background="#4ECDC4", + ) app = app_service.create_app(tenant.id, app_args, account) # Use invalid workflow run ID @@ -637,13 +637,13 @@ class TestWorkflowRunService: tenant = account.current_tenant # Create app - app_args = { - "name": "Test App", - "mode": "chat", - "icon_type": "emoji", - "icon": "🚀", - "icon_background": "#4ECDC4", - } + app_args = CreateAppParams( + name="Test App", + mode="chat", + icon_type="emoji", + icon="🚀", + icon_background="#4ECDC4", + ) app = app_service.create_app(tenant.id, app_args, account) # Create workflow run diff --git a/api/tests/test_containers_integration_tests/services/tools/test_workflow_tools_manage_service.py b/api/tests/test_containers_integration_tests/services/tools/test_workflow_tools_manage_service.py index 21a1975879..9b574fe2df 100644 --- a/api/tests/test_containers_integration_tests/services/tools/test_workflow_tools_manage_service.py +++ b/api/tests/test_containers_integration_tests/services/tools/test_workflow_tools_manage_service.py @@ -11,7 +11,7 @@ from core.tools.errors import WorkflowToolHumanInputNotSupportedError from models.tools import WorkflowToolProvider from models.workflow import Workflow as WorkflowModel from services.account_service import AccountService, TenantService -from services.app_service import AppService +from services.app_service import AppService, CreateAppParams from services.tools.workflow_tools_manage_service import WorkflowToolManageService from tests.test_containers_integration_tests.helpers import generate_valid_password @@ -94,16 +94,16 @@ class TestWorkflowToolManageService: tenant = account.current_tenant # Create app with realistic data - app_args = { - "name": fake.company(), - "description": fake.text(max_nb_chars=100), - "mode": "workflow", - "icon_type": "emoji", - "icon": "🤖", - "icon_background": "#FF6B6B", - "api_rph": 100, - "api_rpm": 10, - } + app_args = CreateAppParams( + name=fake.company(), + description=fake.text(max_nb_chars=100), + mode="workflow", + icon_type="emoji", + icon="🤖", + icon_background="#FF6B6B", + api_rph=100, + api_rpm=10, + ) app_service = AppService() app = app_service.create_app(tenant.id, app_args, account)