From 64cabff42502ff586b0621c5ed9a694ed206a1d2 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 13 Jan 2026 19:53:26 +0900 Subject: [PATCH 1/3] . --- api/models/model.py | 60 ++++++++-------- .../services/test_app_dsl_service.py | 39 ++++++----- .../services/test_workflow_service.py | 70 ++++++++++--------- 3 files changed, 87 insertions(+), 82 deletions(-) diff --git a/api/models/model.py b/api/models/model.py index a48f4d34d4b8cf..61e8c918323508 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -315,40 +315,42 @@ def author_name(self) -> str | None: return None -class AppModelConfig(Base): +class AppModelConfig(TypeBase): __tablename__ = "app_model_configs" __table_args__ = (sa.PrimaryKeyConstraint("id", name="app_model_config_pkey"), sa.Index("app_app_id_idx", "app_id")) - id = mapped_column(StringUUID, default=lambda: str(uuid4())) - app_id = mapped_column(StringUUID, nullable=False) - provider = mapped_column(String(255), nullable=True) - model_id = mapped_column(String(255), nullable=True) - configs = mapped_column(sa.JSON, nullable=True) - created_by = mapped_column(StringUUID, nullable=True) - created_at = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp()) - updated_by = mapped_column(StringUUID, nullable=True) - updated_at = mapped_column( + id: Mapped[str] = mapped_column(StringUUID, default=lambda: str(uuid4())) + app_id: Mapped[str] = mapped_column(StringUUID, nullable=False) + provider: Mapped[str | None] = mapped_column(String(255), nullable=True) + model_id: Mapped[str | None] = mapped_column(String(255), nullable=True) + configs: Mapped[Any | None] = mapped_column(sa.JSON, nullable=True) + created_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True) + created_at: Mapped[datetime] = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True) + updated_at: Mapped[datetime] = mapped_column( sa.DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp() ) - opening_statement = mapped_column(LongText) - suggested_questions = mapped_column(LongText) - suggested_questions_after_answer = mapped_column(LongText) - speech_to_text = mapped_column(LongText) - text_to_speech = mapped_column(LongText) - more_like_this = mapped_column(LongText) - model = mapped_column(LongText) - user_input_form = mapped_column(LongText) - dataset_query_variable = mapped_column(String(255)) - pre_prompt = mapped_column(LongText) - agent_mode = mapped_column(LongText) - sensitive_word_avoidance = mapped_column(LongText) - retriever_resource = mapped_column(LongText) - prompt_type = mapped_column(String(255), nullable=False, server_default=sa.text("'simple'")) - chat_prompt_config = mapped_column(LongText) - completion_prompt_config = mapped_column(LongText) - dataset_configs = mapped_column(LongText) - external_data_tools = mapped_column(LongText) - file_upload = mapped_column(LongText) + opening_statement: Mapped[str | None] = mapped_column(LongText) + suggested_questions: Mapped[str | None] = mapped_column(LongText) + suggested_questions_after_answer: Mapped[str | None] = mapped_column(LongText) + speech_to_text: Mapped[str | None] = mapped_column(LongText) + text_to_speech: Mapped[str | None] = mapped_column(LongText) + more_like_this: Mapped[str | None] = mapped_column(LongText) + model: Mapped[str | None] = mapped_column(LongText) + user_input_form: Mapped[str | None] = mapped_column(LongText) + dataset_query_variable: Mapped[str | None] = mapped_column(String(255)) + pre_prompt: Mapped[str | None] = mapped_column(LongText) + agent_mode: Mapped[str | None] = mapped_column(LongText) + sensitive_word_avoidance: Mapped[str | None] = mapped_column(LongText) + retriever_resource: Mapped[str | None] = mapped_column(LongText) + prompt_type: Mapped[str] = mapped_column( + String(255), nullable=False, server_default=sa.text("'simple'"), default="simple" + ) + chat_prompt_config: Mapped[str | None] = mapped_column(LongText) + completion_prompt_config: Mapped[str | None] = mapped_column(LongText) + dataset_configs: Mapped[str | None] = mapped_column(LongText) + external_data_tools: Mapped[str | None] = mapped_column(LongText) + file_upload: Mapped[str | None] = mapped_column(LongText) @property def app(self) -> App | None: 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 119f92d77276c9..2aa7ed6de2c4db 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 @@ -226,26 +226,27 @@ def test_export_dsl_chat_app_success(self, db_session_with_containers, mock_exte app, account = self._create_test_app_and_account(db_session_with_containers, mock_external_service_dependencies) # Create model config for the app - model_config = AppModelConfig() - model_config.id = fake.uuid4() - model_config.app_id = app.id - model_config.provider = "openai" - model_config.model_id = "gpt-3.5-turbo" - model_config.model = json.dumps( - { - "provider": "openai", - "name": "gpt-3.5-turbo", - "mode": "chat", - "completion_params": { - "max_tokens": 1000, - "temperature": 0.7, - }, - } + model_config = AppModelConfig( + id=fake.uuid4(), + app_id=app.id, + provider="openai", + model_id="gpt-3.5-turbo", + model=json.dumps( + { + "provider": "openai", + "name": "gpt-3.5-turbo", + "mode": "chat", + "completion_params": { + "max_tokens": 1000, + "temperature": 0.7, + }, + } + ), + pre_prompt="You are a helpful assistant.", + prompt_type="simple", + created_by=account.id, + updated_by=account.id, ) - model_config.pre_prompt = "You are a helpful assistant." - model_config.prompt_type = "simple" - model_config.created_by = account.id - model_config.updated_by = account.id # Set the app_model_config_id to link the config app.app_model_config_id = model_config.id diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_service.py index 88c6313f6431ff..1c8921172795be 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_service.py @@ -925,24 +925,25 @@ def test_convert_to_workflow_chat_mode_success(self, db_session_with_containers) # Create app model config (required for conversion) from models.model import AppModelConfig - app_model_config = AppModelConfig() - app_model_config.id = fake.uuid4() - app_model_config.app_id = app.id - app_model_config.tenant_id = app.tenant_id - app_model_config.provider = "openai" - app_model_config.model_id = "gpt-3.5-turbo" - # Set the model field directly - this is what model_dict property returns - app_model_config.model = json.dumps( - { - "provider": "openai", - "name": "gpt-3.5-turbo", - "completion_params": {"max_tokens": 1000, "temperature": 0.7}, - } + app_model_config = AppModelConfig( + id=fake.uuid4(), + app_id=app.id, + tenant_id=app.tenant_id, + provider="openai", + model_id="gpt-3.5-turbo", + # Set the model field directly - this is what model_dict property returns + model=json.dumps( + { + "provider": "openai", + "name": "gpt-3.5-turbo", + "completion_params": {"max_tokens": 1000, "temperature": 0.7}, + } + ), + # Set pre_prompt for PromptTemplateConfigManager + pre_prompt="You are a helpful assistant.", + created_by=account.id, + updated_by=account.id, ) - # Set pre_prompt for PromptTemplateConfigManager - app_model_config.pre_prompt = "You are a helpful assistant." - app_model_config.created_by = account.id - app_model_config.updated_by = account.id from extensions.ext_database import db @@ -987,24 +988,25 @@ def test_convert_to_workflow_completion_mode_success(self, db_session_with_conta # Create app model config (required for conversion) from models.model import AppModelConfig - app_model_config = AppModelConfig() - app_model_config.id = fake.uuid4() - app_model_config.app_id = app.id - app_model_config.tenant_id = app.tenant_id - app_model_config.provider = "openai" - app_model_config.model_id = "gpt-3.5-turbo" - # Set the model field directly - this is what model_dict property returns - app_model_config.model = json.dumps( - { - "provider": "openai", - "name": "gpt-3.5-turbo", - "completion_params": {"max_tokens": 1000, "temperature": 0.7}, - } + app_model_config = AppModelConfig( + id=fake.uuid4(), + app_id=app.id, + tenant_id=app.tenant_id, + provider="openai", + model_id="gpt-3.5-turbo", + # Set the model field directly - this is what model_dict property returns + model=json.dumps( + { + "provider": "openai", + "name": "gpt-3.5-turbo", + "completion_params": {"max_tokens": 1000, "temperature": 0.7}, + } + ), + # Set pre_prompt for PromptTemplateConfigManager + pre_prompt="Complete the following text:", + created_by=account.id, + updated_by=account.id, ) - # Set pre_prompt for PromptTemplateConfigManager - app_model_config.pre_prompt = "Complete the following text:" - app_model_config.created_by = account.id - app_model_config.updated_by = account.id from extensions.ext_database import db From 02835941543a2b5582bbeb8d78541f876e80fc37 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Fri, 16 Jan 2026 14:00:20 +0900 Subject: [PATCH 2/3] , default=None --- api/models/model.py | 62 ++++++++++--------- api/services/app_dsl_service.py | 8 +-- api/services/app_service.py | 7 +-- api/services/message_service.py | 3 +- .../services/test_agent_service.py | 6 +- .../services/test_app_dsl_service.py | 2 +- .../services/test_workflow_service.py | 6 +- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/api/models/model.py b/api/models/model.py index 063659130172f4..847dffd1daf051 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -319,38 +319,44 @@ class AppModelConfig(TypeBase): __tablename__ = "app_model_configs" __table_args__ = (sa.PrimaryKeyConstraint("id", name="app_model_config_pkey"), sa.Index("app_app_id_idx", "app_id")) - id: Mapped[str] = mapped_column(StringUUID, default=lambda: str(uuid4())) + id: Mapped[str] = mapped_column(StringUUID, default=lambda: str(uuid4()), init=False) app_id: Mapped[str] = mapped_column(StringUUID, nullable=False) - provider: Mapped[str | None] = mapped_column(String(255), nullable=True) - model_id: Mapped[str | None] = mapped_column(String(255), nullable=True) - configs: Mapped[Any | None] = mapped_column(sa.JSON, nullable=True) - created_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True) - created_at: Mapped[datetime] = mapped_column(sa.DateTime, nullable=False, server_default=func.current_timestamp()) - updated_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True) + provider: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None) + model_id: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None) + configs: Mapped[Any | None] = mapped_column(sa.JSON, nullable=True, default=None) + created_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None) + created_at: Mapped[datetime] = mapped_column( + sa.DateTime, nullable=False, server_default=func.current_timestamp(), init=False + ) + updated_by: Mapped[str | None] = mapped_column(StringUUID, nullable=True, default=None) updated_at: Mapped[datetime] = mapped_column( - sa.DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp() + sa.DateTime, + nullable=False, + server_default=func.current_timestamp(), + onupdate=func.current_timestamp(), + init=False, ) - opening_statement: Mapped[str | None] = mapped_column(LongText) - suggested_questions: Mapped[str | None] = mapped_column(LongText) - suggested_questions_after_answer: Mapped[str | None] = mapped_column(LongText) - speech_to_text: Mapped[str | None] = mapped_column(LongText) - text_to_speech: Mapped[str | None] = mapped_column(LongText) - more_like_this: Mapped[str | None] = mapped_column(LongText) - model: Mapped[str | None] = mapped_column(LongText) - user_input_form: Mapped[str | None] = mapped_column(LongText) - dataset_query_variable: Mapped[str | None] = mapped_column(String(255)) - pre_prompt: Mapped[str | None] = mapped_column(LongText) - agent_mode: Mapped[str | None] = mapped_column(LongText) - sensitive_word_avoidance: Mapped[str | None] = mapped_column(LongText) - retriever_resource: Mapped[str | None] = mapped_column(LongText) + opening_statement: Mapped[str | None] = mapped_column(LongText, default=None) + suggested_questions: Mapped[str | None] = mapped_column(LongText, default=None) + suggested_questions_after_answer: Mapped[str | None] = mapped_column(LongText, default=None) + speech_to_text: Mapped[str | None] = mapped_column(LongText, default=None) + text_to_speech: Mapped[str | None] = mapped_column(LongText, default=None) + more_like_this: Mapped[str | None] = mapped_column(LongText, default=None) + model: Mapped[str | None] = mapped_column(LongText, default=None) + user_input_form: Mapped[str | None] = mapped_column(LongText, default=None) + dataset_query_variable: Mapped[str | None] = mapped_column(String(255), default=None) + pre_prompt: Mapped[str | None] = mapped_column(LongText, default=None) + agent_mode: Mapped[str | None] = mapped_column(LongText, default=None) + sensitive_word_avoidance: Mapped[str | None] = mapped_column(LongText, default=None) + retriever_resource: Mapped[str | None] = mapped_column(LongText, default=None) prompt_type: Mapped[str] = mapped_column( String(255), nullable=False, server_default=sa.text("'simple'"), default="simple" ) - chat_prompt_config: Mapped[str | None] = mapped_column(LongText) - completion_prompt_config: Mapped[str | None] = mapped_column(LongText) - dataset_configs: Mapped[str | None] = mapped_column(LongText) - external_data_tools: Mapped[str | None] = mapped_column(LongText) - file_upload: Mapped[str | None] = mapped_column(LongText) + chat_prompt_config: Mapped[str | None] = mapped_column(LongText, default=None) + completion_prompt_config: Mapped[str | None] = mapped_column(LongText, default=None) + dataset_configs: Mapped[str | None] = mapped_column(LongText, default=None) + external_data_tools: Mapped[str | None] = mapped_column(LongText, default=None) + file_upload: Mapped[str | None] = mapped_column(LongText, default=None) @property def app(self) -> App | None: @@ -754,8 +760,8 @@ def model_config(self): override_model_configs = json.loads(self.override_model_configs) if "model" in override_model_configs: - app_model_config = AppModelConfig() - app_model_config = app_model_config.from_model_config_dict(override_model_configs) + # where is app_id? + app_model_config = AppModelConfig().from_model_config_dict(override_model_configs) # type: ignore model_config = app_model_config.to_dict() else: model_config["configs"] = override_model_configs diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index acd2a25a863f68..da22464d394c27 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -521,12 +521,10 @@ def _create_or_update_app( raise ValueError("Missing model_config for chat/agent-chat/completion app") # Initialize or update model config if not app.app_model_config: - app_model_config = AppModelConfig().from_model_config_dict(model_config) + app_model_config = AppModelConfig( + app_id=app.id, created_by=account.id, updated_by=account.id + ).from_model_config_dict(model_config) app_model_config.id = str(uuid4()) - app_model_config.app_id = app.id - app_model_config.created_by = account.id - app_model_config.updated_by = account.id - app.app_model_config_id = app_model_config.id self._session.add(app_model_config) diff --git a/api/services/app_service.py b/api/services/app_service.py index 02ebfbace0cdb2..af458ff6187faf 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -150,10 +150,9 @@ def create_app(self, tenant_id: str, args: dict, account: Account) -> App: db.session.flush() if default_model_config: - app_model_config = AppModelConfig(**default_model_config) - app_model_config.app_id = app.id - app_model_config.created_by = account.id - app_model_config.updated_by = account.id + app_model_config = AppModelConfig( + **default_model_config, app_id=app.id, created_by=account.id, updated_by=account.id + ) db.session.add(app_model_config) db.session.flush() diff --git a/api/services/message_service.py b/api/services/message_service.py index e1a256e64d8600..a53ca8b22da54e 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -261,10 +261,9 @@ def get_suggested_questions_after_answer( else: conversation_override_model_configs = json.loads(conversation.override_model_configs) app_model_config = AppModelConfig( - id=conversation.app_model_config_id, app_id=app_model.id, ) - + app_model_config.id = conversation.app_model_config_id app_model_config = app_model_config.from_model_config_dict(conversation_override_model_configs) if not app_model_config: raise ValueError("did not find app model config") 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 a22d6f8fbff9ca..6eedbd6cfae10b 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 @@ -172,7 +172,6 @@ def _create_test_conversation_and_message(self, db_session_with_containers, app, # Create app model config app_model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, provider="openai", model_id="gpt-3.5-turbo", @@ -180,6 +179,7 @@ def _create_test_conversation_and_message(self, db_session_with_containers, app, model="gpt-3.5-turbo", agent_mode=json.dumps({"enabled": True, "strategy": "react", "tools": []}), ) + app_model_config.id = fake.uuid4() db.session.add(app_model_config) db.session.commit() @@ -413,7 +413,6 @@ def test_get_agent_logs_with_end_user(self, db_session_with_containers, mock_ext # Create app model config app_model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, provider="openai", model_id="gpt-3.5-turbo", @@ -421,6 +420,7 @@ def test_get_agent_logs_with_end_user(self, db_session_with_containers, mock_ext model="gpt-3.5-turbo", agent_mode=json.dumps({"enabled": True, "strategy": "react", "tools": []}), ) + app_model_config.id = fake.uuid4() db.session.add(app_model_config) db.session.commit() @@ -485,7 +485,6 @@ def test_get_agent_logs_with_unknown_executor(self, db_session_with_containers, # Create app model config app_model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, provider="openai", model_id="gpt-3.5-turbo", @@ -493,6 +492,7 @@ def test_get_agent_logs_with_unknown_executor(self, db_session_with_containers, model="gpt-3.5-turbo", agent_mode=json.dumps({"enabled": True, "strategy": "react", "tools": []}), ) + app_model_config.id = fake.uuid4() db.session.add(app_model_config) db.session.commit() 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 2aa7ed6de2c4db..e2a450b90ce2c7 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 @@ -227,7 +227,6 @@ def test_export_dsl_chat_app_success(self, db_session_with_containers, mock_exte # Create model config for the app model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, provider="openai", model_id="gpt-3.5-turbo", @@ -247,6 +246,7 @@ def test_export_dsl_chat_app_success(self, db_session_with_containers, mock_exte created_by=account.id, updated_by=account.id, ) + model_config.id = fake.uuid4() # Set the app_model_config_id to link the config app.app_model_config_id = model_config.id diff --git a/api/tests/test_containers_integration_tests/services/test_workflow_service.py b/api/tests/test_containers_integration_tests/services/test_workflow_service.py index 1c8921172795be..cb691d5c3dc277 100644 --- a/api/tests/test_containers_integration_tests/services/test_workflow_service.py +++ b/api/tests/test_containers_integration_tests/services/test_workflow_service.py @@ -926,9 +926,7 @@ def test_convert_to_workflow_chat_mode_success(self, db_session_with_containers) from models.model import AppModelConfig app_model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, - tenant_id=app.tenant_id, provider="openai", model_id="gpt-3.5-turbo", # Set the model field directly - this is what model_dict property returns @@ -944,6 +942,7 @@ def test_convert_to_workflow_chat_mode_success(self, db_session_with_containers) created_by=account.id, updated_by=account.id, ) + app_model_config.id = fake.uuid4() from extensions.ext_database import db @@ -989,9 +988,7 @@ def test_convert_to_workflow_completion_mode_success(self, db_session_with_conta from models.model import AppModelConfig app_model_config = AppModelConfig( - id=fake.uuid4(), app_id=app.id, - tenant_id=app.tenant_id, provider="openai", model_id="gpt-3.5-turbo", # Set the model field directly - this is what model_dict property returns @@ -1007,6 +1004,7 @@ def test_convert_to_workflow_completion_mode_success(self, db_session_with_conta created_by=account.id, updated_by=account.id, ) + app_model_config.id = fake.uuid4() from extensions.ext_database import db From 4d06df9d044b990bf9a91897614c0a889ce7c0a3 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 20 Jan 2026 09:41:33 +0900 Subject: [PATCH 3/3] Update api/models/model.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/models/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/models/model.py b/api/models/model.py index 847dffd1daf051..06167bc95ba7a0 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -761,7 +761,7 @@ def model_config(self): if "model" in override_model_configs: # where is app_id? - app_model_config = AppModelConfig().from_model_config_dict(override_model_configs) # type: ignore + app_model_config = AppModelConfig(app_id=self.app_id).from_model_config_dict(override_model_configs) model_config = app_model_config.to_dict() else: model_config["configs"] = override_model_configs