From 915cb54f2130cb8b0479d7508763cda0f42b27b6 Mon Sep 17 00:00:00 2001 From: wxy Date: Thu, 16 Apr 2026 17:54:50 +0800 Subject: [PATCH 1/5] feat(tenant): add public subscription plan list endpoint and enhance plan information Add a public subscription plan list endpoint that can be accessed without authentication. Enhance the returned subscription plan information fields, including multi-language support and default free plan fallback logic. Additionally, implement automatic model binding for the knowledge base service. --- api/app/config/default_free_plan.py | 5 + api/app/controllers/__init__.py | 1 + .../tenant_subscription_controller.py | 95 ++++++++++++++++++- api/app/services/knowledge_service.py | 51 +++++++++- 4 files changed, 148 insertions(+), 4 deletions(-) diff --git a/api/app/config/default_free_plan.py b/api/app/config/default_free_plan.py index 23a3a10e..4b357236 100644 --- a/api/app/config/default_free_plan.py +++ b/api/app/config/default_free_plan.py @@ -5,6 +5,7 @@ DEFAULT_FREE_PLAN = { "name": "记忆体验版", + "name_en": "Memory Experience", "category": "saas_personal", "tier_level": 0, "version": "1.0", @@ -12,9 +13,13 @@ DEFAULT_FREE_PLAN = { "price": 0, "billing_cycle": "permanent_free", "core_value": "感受永久记忆", + "core_value_en": "Experience Permanent Memory", "tech_support": "社群交流", + "tech_support_en": "Community Support", "sla_compliance": "无", + "sla_compliance_en": "None", "page_customization": "无", + "page_customization_en": "None", "theme_color": "#64748B", "quotas": { "workspace_quota": 1, diff --git a/api/app/controllers/__init__.py b/api/app/controllers/__init__.py index 377205c4..e9417d68 100644 --- a/api/app/controllers/__init__.py +++ b/api/app/controllers/__init__.py @@ -100,5 +100,6 @@ manager_router.include_router(ontology_controller.router) manager_router.include_router(skill_controller.router) manager_router.include_router(i18n_controller.router) manager_router.include_router(tenant_subscription_controller.router) +manager_router.include_router(tenant_subscription_controller.public_router) __all__ = ["manager_router"] diff --git a/api/app/controllers/tenant_subscription_controller.py b/api/app/controllers/tenant_subscription_controller.py index c3fde572..141d48a8 100644 --- a/api/app/controllers/tenant_subscription_controller.py +++ b/api/app/controllers/tenant_subscription_controller.py @@ -2,7 +2,7 @@ 租户套餐查询接口(普通用户可访问) """ import datetime -from typing import Callable +from typing import Callable, Optional from fastapi import APIRouter, Depends from fastapi.responses import JSONResponse @@ -19,6 +19,7 @@ from app.schemas.response_schema import ApiResponse logger = get_api_logger() router = APIRouter(prefix="/tenant", tags=["Tenant"]) +public_router = APIRouter(tags=["Tenant"]) @router.get("/subscription", response_model=ApiResponse, summary="获取当前用户所属租户的套餐信息") @@ -42,7 +43,41 @@ async def get_my_tenant_subscription( sub = svc.get_subscription(tenant_id) if not sub: - return success(data=None, msg="暂无有效套餐") + # 无订阅记录时,兜底返回免费套餐信息 + free_plan = svc.plan_repo.get_free_plan() + if not free_plan: + return success(data=None, msg="暂无有效套餐") + return success(data={ + "subscription_id": None, + "tenant_id": str(tenant_id), + "package_plan_id": str(free_plan.id), + "package_version": free_plan.version, + "package_plan": { + "id": str(free_plan.id), + "name": free_plan.name, + "name_en": free_plan.name_en, + "version": free_plan.version, + "category": free_plan.category, + "tier_level": free_plan.tier_level, + "price": float(free_plan.price) if free_plan.price is not None else 0.0, + "billing_cycle": free_plan.billing_cycle, + "core_value": free_plan.core_value, + "core_value_en": free_plan.core_value_en, + "tech_support": free_plan.tech_support, + "tech_support_en": free_plan.tech_support_en, + "sla_compliance": free_plan.sla_compliance, + "sla_compliance_en": free_plan.sla_compliance_en, + "page_customization": free_plan.page_customization, + "page_customization_en": free_plan.page_customization_en, + "theme_color": free_plan.theme_color, + }, + "started_at": None, + "expired_at": None, + "status": "active", + "quota": free_plan.quotas or {}, + "created_at": int(datetime.datetime.utcnow().timestamp() * 1000), + "updated_at": int(datetime.datetime.utcnow().timestamp() * 1000), + }, msg="免费套餐") return success(data=svc.build_response(sub)) @@ -62,11 +97,21 @@ async def get_my_tenant_subscription( "package_plan": { "id": None, "name": plan["name"], + "name_en": plan.get("name_en"), "version": plan["version"], "category": plan["category"], "tier_level": plan["tier_level"], "price": float(plan["price"]), "billing_cycle": plan["billing_cycle"], + "core_value": plan.get("core_value"), + "core_value_en": plan.get("core_value_en"), + "tech_support": plan.get("tech_support"), + "tech_support_en": plan.get("tech_support_en"), + "sla_compliance": plan.get("sla_compliance"), + "sla_compliance_en": plan.get("sla_compliance_en"), + "page_customization": plan.get("page_customization"), + "page_customization_en": plan.get("page_customization_en"), + "theme_color": plan.get("theme_color"), }, "started_at": None, "expired_at": None, @@ -80,3 +125,49 @@ async def get_my_tenant_subscription( except Exception as e: logger.error(f"获取租户套餐信息失败: {e}", exc_info=True) return JSONResponse(status_code=500, content=fail(code=500, msg="获取套餐信息失败")) + + +@public_router.get("/package-plans", response_model=ApiResponse, summary="获取套餐列表(公开)") +async def list_package_plans_public( + category: Optional[str] = None, + status: Optional[bool] = None, + search: Optional[str] = None, + db: Session = Depends(get_db), +): + """ + 公开接口,无需鉴权。 + SaaS 版从数据库读取套餐列表;社区版降级返回 default_free_plan.py 中的免费套餐。 + """ + try: + from premium.platform_admin.package_plan_service import PackagePlanService + from premium.platform_admin.package_plan_schema import PackagePlanResponse + svc = PackagePlanService(db) + result = svc.get_list(page=1, size=9999, category=category, status=status, search=search) + return success(data=[PackagePlanResponse.model_validate(p).model_dump(mode="json") for p in result["items"]]) + except ModuleNotFoundError: + from app.config.default_free_plan import DEFAULT_FREE_PLAN + plan = DEFAULT_FREE_PLAN + return success(data=[{ + "id": None, + "name": plan["name"], + "name_en": plan.get("name_en"), + "version": plan["version"], + "category": plan["category"], + "tier_level": plan["tier_level"], + "price": float(plan["price"]), + "billing_cycle": plan["billing_cycle"], + "core_value": plan.get("core_value"), + "core_value_en": plan.get("core_value_en"), + "tech_support": plan.get("tech_support"), + "tech_support_en": plan.get("tech_support_en"), + "sla_compliance": plan.get("sla_compliance"), + "sla_compliance_en": plan.get("sla_compliance_en"), + "page_customization": plan.get("page_customization"), + "page_customization_en": plan.get("page_customization_en"), + "theme_color": plan.get("theme_color"), + "status": plan.get("status", True), + "quota": plan["quotas"], + }]) + except Exception as e: + logger.error(f"获取套餐列表失败: {e}", exc_info=True) + return JSONResponse(status_code=500, content=fail(code=500, msg="获取套餐列表失败")) diff --git a/api/app/services/knowledge_service.py b/api/app/services/knowledge_service.py index bac02e96..46108efd 100644 --- a/api/app/services/knowledge_service.py +++ b/api/app/services/knowledge_service.py @@ -2,11 +2,14 @@ import uuid from sqlalchemy.orm import Session from app.models.user_model import User from app.models.knowledge_model import Knowledge +from app.models.workspace_model import Workspace +from app.models.models_model import ModelConfig from app.schemas.knowledge_schema import KnowledgeCreate, KnowledgeUpdate from app.repositories import knowledge_repository from app.core.logging_config import get_business_logger +from app.repositories.model_repository import ModelConfigRepository +from app.models.models_model import ModelType -# Obtain a dedicated logger for business logic business_logger = get_business_logger() @@ -60,13 +63,57 @@ def create_knowledge( db: Session, knowledge: KnowledgeCreate, current_user: User ) -> Knowledge: business_logger.info(f"Create a knowledge base: {knowledge.name}, creator: {current_user.username}") - + try: knowledge.created_by = current_user.id if knowledge.workspace_id is None: knowledge.workspace_id = current_user.current_workspace_id if knowledge.parent_id is None: knowledge.parent_id = knowledge.workspace_id + + workspace = db.query(Workspace).filter(Workspace.id == knowledge.workspace_id).first() + if not workspace: + raise Exception(f"Workspace {knowledge.workspace_id} not found") + + tenant_id = workspace.tenant_id + + if not knowledge.embedding_id: + embedding_models = ModelConfigRepository.get_by_type( + db=db, model_types=[ModelType.EMBEDDING], tenant_id=tenant_id, is_active=True + ) + if embedding_models: + knowledge.embedding_id = embedding_models[0].id + business_logger.debug(f"Auto-bind embedding model: {embedding_models[0].id}") + + if not knowledge.reranker_id: + rerank_models = ModelConfigRepository.get_by_type( + db=db, model_types=[ModelType.RERANK], tenant_id=tenant_id, is_active=True + ) + if rerank_models: + knowledge.reranker_id = rerank_models[0].id + business_logger.debug(f"Auto-bind rerank model: {rerank_models[0].id}") + + if not knowledge.llm_id: + llm_models = ModelConfigRepository.get_by_type( + db=db, model_types=[ModelType.LLM, ModelType.CHAT], tenant_id=tenant_id, is_active=True + ) + if llm_models: + knowledge.llm_id = llm_models[0].id + business_logger.debug(f"Auto-bind llm model: {llm_models[0].id}") + + if not knowledge.image2text_id: + image2text_models = db.query(ModelConfig).filter( + ModelConfig.tenant_id == tenant_id, + ModelConfig.type.in_([ModelType.CHAT.value, ModelType.IMAGE.value]), + ModelConfig.capability.contains(["vision"]), + ModelConfig.is_active == True, + ModelConfig.is_composite == False + ).order_by(ModelConfig.created_at.desc()).all() + if not image2text_models: + raise Exception("租户下没有可用的视觉模型,创建知识库失败") + knowledge.image2text_id = image2text_models[0].id + business_logger.debug(f"Auto-bind image2text model: {image2text_models[0].id}") + business_logger.debug(f"Start creating the knowledge base: {knowledge.name}") db_knowledge = knowledge_repository.create_knowledge( db=db, knowledge=knowledge From f883c1469d3df260e01834c22a8f4e0bef3c0a1a Mon Sep 17 00:00:00 2001 From: wxy Date: Thu, 16 Apr 2026 19:35:52 +0800 Subject: [PATCH 2/5] feat(quota management): add end-user quota check for shared conversations fix(default free plan): adjust free plan quota limits feat(application service): add functionality to reset Agent model parameters to default values --- api/app/config/default_free_plan.py | 16 ++--- api/app/controllers/app_controller.py | 14 +++++ .../controllers/public_share_controller.py | 2 + api/app/core/quota_manager.py | 12 ++++ api/app/services/app_service.py | 61 +++++++++++++++++++ 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/api/app/config/default_free_plan.py b/api/app/config/default_free_plan.py index 4b357236..cb975dcc 100644 --- a/api/app/config/default_free_plan.py +++ b/api/app/config/default_free_plan.py @@ -22,14 +22,14 @@ DEFAULT_FREE_PLAN = { "page_customization_en": "None", "theme_color": "#64748B", "quotas": { - "workspace_quota": 1, - "skill_quota": 5, - "app_quota": 2, - "knowledge_capacity_quota": 0.3, - "memory_engine_quota": 1, - "end_user_quota": 1, - "ontology_project_quota": 3, - "model_quota": 1, + "workspace_quota": 10, + "skill_quota": 50, + "app_quota": 20, + "knowledge_capacity_quota": 30, + "memory_engine_quota": 10, + "end_user_quota": 50, + "ontology_project_quota": 30, + "model_quota": 10, "api_ops_rate_limit": 50, }, } diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index 34449bb5..f7fdc90f 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -271,6 +271,20 @@ def update_agent_config( return success(data=app_schema.AgentConfig.model_validate(cfg)) +@router.post("/{app_id}/config/reset", summary="重置 Agent 配置为默认状态") +@cur_workspace_access_guard() +def reset_agent_config( + app_id: uuid.UUID, + db: Session = Depends(get_db), + current_user=Depends(get_current_user), +): + workspace_id = current_user.current_workspace_id + service = AppService(db) + cfg = service.reset_agent_config(app_id=app_id, workspace_id=workspace_id) + cfg = enrich_agent_config(cfg) + return success(data=app_schema.AgentConfig.model_validate(cfg), msg="Agent 配置已重置为默认状态") + + @router.get("/{app_id}/config", summary="获取 Agent 配置") @cur_workspace_access_guard() def get_agent_config( diff --git a/api/app/controllers/public_share_controller.py b/api/app/controllers/public_share_controller.py index ddd31071..049535b5 100644 --- a/api/app/controllers/public_share_controller.py +++ b/api/app/controllers/public_share_controller.py @@ -10,6 +10,7 @@ from sqlalchemy.orm import Session from app.core.error_codes import BizCode from app.core.exceptions import BusinessException from app.core.logging_config import get_business_logger +from app.core.quota_manager import check_end_user_quota from app.core.response_utils import success, fail from app.db import get_db, get_db_read from app.dependencies import get_share_user_id, ShareTokenData @@ -308,6 +309,7 @@ def get_conversation( "/chat", summary="发送消息(支持流式和非流式)" ) +@check_end_user_quota async def chat( payload: conversation_schema.ChatRequest, share_data: ShareTokenData = Depends(get_share_user_id), diff --git a/api/app/core/quota_manager.py b/api/app/core/quota_manager.py index 6c02ac7a..0e0053a0 100644 --- a/api/app/core/quota_manager.py +++ b/api/app/core/quota_manager.py @@ -55,6 +55,18 @@ def _get_tenant_id_from_kwargs(db: Session, kwargs: dict): if workspace: return workspace.tenant_id + share_data = kwargs.get("share_data") + if share_data and hasattr(share_data, 'share_token'): + from app.models.workspace_model import Workspace + from app.models.app_model import App + share_token = share_data.share_token + from app.models.release_share_model import ReleaseShare + share_record = db.query(ReleaseShare).filter(ReleaseShare.share_token == share_token).first() + if share_record: + app = db.query(App).filter(App.id == share_record.app_id, App.is_active.is_(True)).first() + if app: + return app.workspace.tenant_id + return None diff --git a/api/app/services/app_service.py b/api/app/services/app_service.py index 534ab8d0..549af761 100644 --- a/api/app/services/app_service.py +++ b/api/app/services/app_service.py @@ -1452,6 +1452,67 @@ class AppService: logger.debug("配置不存在,返回默认模板", extra={"app_id": str(app_id)}) return self._create_default_agent_config(app_id) + def reset_agent_config( + self, + *, + app_id: uuid.UUID, + workspace_id: Optional[uuid.UUID] = None + ) -> AgentConfig: + """仅将 Agent 模型参数重置为默认值(不影响其他配置) + + Args: + app_id: 应用ID + workspace_id: 工作空间ID(用于权限验证) + + Returns: + AgentConfig: 重置后的配置对象 + """ + logger.info("重置 Agent 模型参数为默认值", extra={"app_id": str(app_id)}) + + app = self._get_app_or_404(app_id) + + if app.type != "agent": + raise BusinessException("只有 Agent 类型应用支持 Agent 配置", BizCode.APP_TYPE_NOT_SUPPORTED) + + self._validate_app_writable(app, workspace_id) + + stmt = select(AgentConfig).where(AgentConfig.app_id == app_id, AgentConfig.is_active.is_(True)).order_by( + AgentConfig.updated_at.desc()) + agent_cfg: Optional[AgentConfig] = self.db.scalars(stmt).first() + now = datetime.datetime.now() + + default_model_parameters = { + "temperature": 0.7, + "max_tokens": 2000, + "top_p": 1.0, + "frequency_penalty": 0.0, + "presence_penalty": 0.0, + "n": 1, + "stop": None + } + + if agent_cfg: + agent_cfg.default_model_config_id = None + agent_cfg.model_parameters = default_model_parameters + agent_cfg.updated_at = now + else: + agent_cfg = AgentConfig( + id=uuid.uuid4(), + app_id=app_id, + default_model_config_id=None, + model_parameters=default_model_parameters, + is_active=True, + created_at=now, + updated_at=now, + ) + self.db.add(agent_cfg) + + self.db.commit() + self.db.refresh(agent_cfg) + + logger.info("Agent 模型参数重置成功", extra={"app_id": str(app_id)}) + return agent_cfg + def _create_default_agent_config(self, app_id: uuid.UUID) -> AgentConfig: """创建默认的 Agent 配置模板(不保存到数据库) From 26a3d8a41b362359c520a2b3394a927d07dd2a9e Mon Sep 17 00:00:00 2001 From: wxy Date: Fri, 17 Apr 2026 11:00:22 +0800 Subject: [PATCH 3/5] refactor(agent): refactor Agent model parameters reset logic and add environment variable support Split reset_agent_config into two independent methods for getting and resetting model parameters Add functionality to read quota configuration from environment variables to the default free tier --- api/app/config/default_free_plan.py | 102 ++++++++++++++++++-------- api/app/controllers/app_controller.py | 9 +-- api/app/services/app_service.py | 48 ++++++++---- 3 files changed, 109 insertions(+), 50 deletions(-) diff --git a/api/app/config/default_free_plan.py b/api/app/config/default_free_plan.py index cb975dcc..409b4f7b 100644 --- a/api/app/config/default_free_plan.py +++ b/api/app/config/default_free_plan.py @@ -1,35 +1,77 @@ """ 社区版默认免费套餐配置 当无法从 SaaS 版获取 premium 模块时,使用此配置作为兜底 + +可通过环境变量覆盖配额配置,格式:QUOTA_ +例如:QUOTA_END_USER_QUOTA=100 """ -DEFAULT_FREE_PLAN = { - "name": "记忆体验版", - "name_en": "Memory Experience", - "category": "saas_personal", - "tier_level": 0, - "version": "1.0", - "status": True, - "price": 0, - "billing_cycle": "permanent_free", - "core_value": "感受永久记忆", - "core_value_en": "Experience Permanent Memory", - "tech_support": "社群交流", - "tech_support_en": "Community Support", - "sla_compliance": "无", - "sla_compliance_en": "None", - "page_customization": "无", - "page_customization_en": "None", - "theme_color": "#64748B", - "quotas": { - "workspace_quota": 10, - "skill_quota": 50, - "app_quota": 20, - "knowledge_capacity_quota": 30, - "memory_engine_quota": 10, - "end_user_quota": 50, - "ontology_project_quota": 30, - "model_quota": 10, - "api_ops_rate_limit": 50, - }, -} +import os + + +def _get_quota_from_env(): + """从环境变量获取配额配置""" + quota_keys = [ + "workspace_quota", + "skill_quota", + "app_quota", + "knowledge_capacity_quota", + "memory_engine_quota", + "end_user_quota", + "ontology_project_quota", + "model_quota", + "api_ops_rate_limit", + ] + quotas = {} + for key in quota_keys: + env_key = f"QUOTA_{key.upper()}" + env_value = os.getenv(env_key) + if env_value is not None: + try: + quotas[key] = float(env_value) if '.' in env_value else int(env_value) + except ValueError: + pass + return quotas + + +def _build_default_free_plan(): + """构建默认免费套餐配置""" + base = { + "name": "记忆体验版", + "name_en": "Memory Experience", + "category": "saas_personal", + "tier_level": 0, + "version": "1.0", + "status": True, + "price": 0, + "billing_cycle": "permanent_free", + "core_value": "感受永久记忆", + "core_value_en": "Experience Permanent Memory", + "tech_support": "社群交流", + "tech_support_en": "Community Support", + "sla_compliance": "无", + "sla_compliance_en": "None", + "page_customization": "无", + "page_customization_en": "None", + "theme_color": "#64748B", + "quotas": { + "workspace_quota": 1, + "skill_quota": 5, + "app_quota": 2, + "knowledge_capacity_quota": 0.3, + "memory_engine_quota": 1, + "end_user_quota": 1, + "ontology_project_quota": 3, + "model_quota": 1, + "api_ops_rate_limit": 50, + }, + } + + env_quotas = _get_quota_from_env() + if env_quotas: + base["quotas"].update(env_quotas) + + return base + + +DEFAULT_FREE_PLAN = _build_default_free_plan() diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index f7fdc90f..e13d3c2b 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -271,18 +271,17 @@ def update_agent_config( return success(data=app_schema.AgentConfig.model_validate(cfg)) -@router.post("/{app_id}/config/reset", summary="重置 Agent 配置为默认状态") +@router.post("/{app_id}/model/parameters/reset", summary="获取 Agent 模型参数默认配置") @cur_workspace_access_guard() -def reset_agent_config( +def reset_agent_model_parameters( app_id: uuid.UUID, db: Session = Depends(get_db), current_user=Depends(get_current_user), ): workspace_id = current_user.current_workspace_id service = AppService(db) - cfg = service.reset_agent_config(app_id=app_id, workspace_id=workspace_id) - cfg = enrich_agent_config(cfg) - return success(data=app_schema.AgentConfig.model_validate(cfg), msg="Agent 配置已重置为默认状态") + model_parameters = service.get_default_model_parameters(app_id=app_id) + return success(data=model_parameters, msg="获取 Agent 模型参数默认配置") @router.get("/{app_id}/config", summary="获取 Agent 配置") diff --git a/api/app/services/app_service.py b/api/app/services/app_service.py index 549af761..c4023b0e 100644 --- a/api/app/services/app_service.py +++ b/api/app/services/app_service.py @@ -1452,20 +1452,46 @@ class AppService: logger.debug("配置不存在,返回默认模板", extra={"app_id": str(app_id)}) return self._create_default_agent_config(app_id) + def get_default_model_parameters( + self, + *, + app_id: uuid.UUID, + ) -> "ModelParameters": + """获取 Agent 默认模型参数(不修改数据库) + + Args: + app_id: 应用ID + + Returns: + ModelParameters: 默认模型参数 + """ + logger.info("获取 Agent 默认模型参数", extra={"app_id": str(app_id)}) + + app = self._get_app_or_404(app_id) + + if app.type != "agent": + raise BusinessException("只有 Agent 类型应用支持 Agent 配置", BizCode.APP_TYPE_NOT_SUPPORTED) + + from app.schemas.app_schema import ModelParameters + default_model_parameters = ModelParameters() + + logger.info("获取 Agent 默认模型参数成功", extra={"app_id": str(app_id)}) + return default_model_parameters + def reset_agent_config( self, *, app_id: uuid.UUID, workspace_id: Optional[uuid.UUID] = None - ) -> AgentConfig: - """仅将 Agent 模型参数重置为默认值(不影响其他配置) + ) -> "ModelParameters": + """将 Agent 模型参数重置为默认值(不影响其他配置) Args: app_id: 应用ID workspace_id: 工作空间ID(用于权限验证) Returns: - AgentConfig: 重置后的配置对象 + ModelParameters: 重置后的模型参数 """ logger.info("重置 Agent 模型参数为默认值", extra={"app_id": str(app_id)}) @@ -1476,21 +1502,14 @@ class AppService: self._validate_app_writable(app, workspace_id) + from app.schemas.app_schema import ModelParameters + default_model_parameters = ModelParameters() + stmt = select(AgentConfig).where(AgentConfig.app_id == app_id, AgentConfig.is_active.is_(True)).order_by( AgentConfig.updated_at.desc()) agent_cfg: Optional[AgentConfig] = self.db.scalars(stmt).first() now = datetime.datetime.now() - default_model_parameters = { - "temperature": 0.7, - "max_tokens": 2000, - "top_p": 1.0, - "frequency_penalty": 0.0, - "presence_penalty": 0.0, - "n": 1, - "stop": None - } - if agent_cfg: agent_cfg.default_model_config_id = None agent_cfg.model_parameters = default_model_parameters @@ -1508,10 +1527,9 @@ class AppService: self.db.add(agent_cfg) self.db.commit() - self.db.refresh(agent_cfg) logger.info("Agent 模型参数重置成功", extra={"app_id": str(app_id)}) - return agent_cfg + return default_model_parameters def _create_default_agent_config(self, app_id: uuid.UUID) -> AgentConfig: """创建默认的 Agent 配置模板(不保存到数据库) From a5613314b8bc85d78694943ba9e08b70f0424dc3 Mon Sep 17 00:00:00 2001 From: wxy Date: Fri, 17 Apr 2026 11:34:11 +0800 Subject: [PATCH 4/5] =?UTF-8?q?refactor(agent):=20=E5=B0=86=E9=87=8D?= =?UTF-8?q?=E7=BD=AE=E6=A8=A1=E5=9E=8B=E5=8F=82=E6=95=B0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=8E=B7=E5=8F=96=E9=BB=98=E8=AE=A4=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除不再使用的重置模型参数功能,将POST接口改为GET接口以获取默认参数 --- api/app/controllers/app_controller.py | 4 +- api/app/services/app_service.py | 53 --------------------------- 2 files changed, 2 insertions(+), 55 deletions(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index e13d3c2b..3d97f2a2 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -271,9 +271,9 @@ def update_agent_config( return success(data=app_schema.AgentConfig.model_validate(cfg)) -@router.post("/{app_id}/model/parameters/reset", summary="获取 Agent 模型参数默认配置") +@router.get("/{app_id}/model/parameters/default", summary="获取 Agent 模型参数默认配置") @cur_workspace_access_guard() -def reset_agent_model_parameters( +def get_agent_model_parameters( app_id: uuid.UUID, db: Session = Depends(get_db), current_user=Depends(get_current_user), diff --git a/api/app/services/app_service.py b/api/app/services/app_service.py index c4023b0e..64651189 100644 --- a/api/app/services/app_service.py +++ b/api/app/services/app_service.py @@ -1478,59 +1478,6 @@ class AppService: logger.info("获取 Agent 默认模型参数成功", extra={"app_id": str(app_id)}) return default_model_parameters - def reset_agent_config( - self, - *, - app_id: uuid.UUID, - workspace_id: Optional[uuid.UUID] = None - ) -> "ModelParameters": - """将 Agent 模型参数重置为默认值(不影响其他配置) - - Args: - app_id: 应用ID - workspace_id: 工作空间ID(用于权限验证) - - Returns: - ModelParameters: 重置后的模型参数 - """ - logger.info("重置 Agent 模型参数为默认值", extra={"app_id": str(app_id)}) - - app = self._get_app_or_404(app_id) - - if app.type != "agent": - raise BusinessException("只有 Agent 类型应用支持 Agent 配置", BizCode.APP_TYPE_NOT_SUPPORTED) - - self._validate_app_writable(app, workspace_id) - - from app.schemas.app_schema import ModelParameters - default_model_parameters = ModelParameters() - - stmt = select(AgentConfig).where(AgentConfig.app_id == app_id, AgentConfig.is_active.is_(True)).order_by( - AgentConfig.updated_at.desc()) - agent_cfg: Optional[AgentConfig] = self.db.scalars(stmt).first() - now = datetime.datetime.now() - - if agent_cfg: - agent_cfg.default_model_config_id = None - agent_cfg.model_parameters = default_model_parameters - agent_cfg.updated_at = now - else: - agent_cfg = AgentConfig( - id=uuid.uuid4(), - app_id=app_id, - default_model_config_id=None, - model_parameters=default_model_parameters, - is_active=True, - created_at=now, - updated_at=now, - ) - self.db.add(agent_cfg) - - self.db.commit() - - logger.info("Agent 模型参数重置成功", extra={"app_id": str(app_id)}) - return default_model_parameters - def _create_default_agent_config(self, app_id: uuid.UUID) -> AgentConfig: """创建默认的 Agent 配置模板(不保存到数据库) From 696b2d2417f81e7e202df675768d495015479c41 Mon Sep 17 00:00:00 2001 From: wxy Date: Fri, 17 Apr 2026 11:38:45 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix(knowledge=5Fservice):=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E7=9F=A5=E8=AF=86=E5=88=9B=E5=BB=BA=E6=97=B6=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=B1=BB=E5=9E=8B=E8=BF=87=E6=BB=A4=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除IMAGE类型过滤,仅保留CHAT类型,确保只筛选出支持视觉能力的聊天模型 --- api/app/services/knowledge_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/services/knowledge_service.py b/api/app/services/knowledge_service.py index 46108efd..94653db8 100644 --- a/api/app/services/knowledge_service.py +++ b/api/app/services/knowledge_service.py @@ -104,7 +104,7 @@ def create_knowledge( if not knowledge.image2text_id: image2text_models = db.query(ModelConfig).filter( ModelConfig.tenant_id == tenant_id, - ModelConfig.type.in_([ModelType.CHAT.value, ModelType.IMAGE.value]), + ModelConfig.type.in_([ModelType.CHAT.value]), ModelConfig.capability.contains(["vision"]), ModelConfig.is_active == True, ModelConfig.is_composite == False