mirror of
https://github.com/langgenius/dify.git
synced 2026-07-31 09:19:29 +08:00
feat(api): expose app mode in webapp site response (#39607)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
aeda37db68
commit
7e6ba05464
@ -24,7 +24,7 @@ from extensions.ext_database import db
|
||||
from fields.base import ResponseModel
|
||||
from libs.helper import RateLimiter, dump_response, extract_remote_ip, to_timestamp
|
||||
from models.account import TenantStatus
|
||||
from models.model import App, Site
|
||||
from models.model import App, AppMode, Site
|
||||
from repositories.factory import DifyAPIRepositoryFactory
|
||||
from services.feature_service import FeatureService
|
||||
from services.human_input_file_upload_service import HumanInputFileUploadService
|
||||
@ -207,6 +207,7 @@ class HumanInputFormApi(Resource):
|
||||
site=WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.value_of(app_model.mode),
|
||||
site=site,
|
||||
end_user_id=None,
|
||||
features=features,
|
||||
|
||||
@ -14,7 +14,7 @@ from extensions.storage.storage_type import StorageType
|
||||
from fields.base import ResponseModel
|
||||
from libs.helper import build_icon_url
|
||||
from models.account import Tenant, TenantStatus
|
||||
from models.model import App, EndUser, IconType, Site
|
||||
from models.model import App, AppMode, EndUser, IconType, Site
|
||||
from services.feature_service import FeatureModel, FeatureService
|
||||
from services.file_service import FileService
|
||||
|
||||
@ -67,6 +67,7 @@ class WebAppCustomConfigResponse(ResponseModel):
|
||||
|
||||
class WebAppSiteResponse(ResponseModel):
|
||||
app_id: str
|
||||
mode: AppMode
|
||||
end_user_id: str | None = None
|
||||
enable_site: bool
|
||||
site: WebSiteResponse
|
||||
@ -83,6 +84,7 @@ class WebAppSiteResponse(ResponseModel):
|
||||
*,
|
||||
tenant: Tenant,
|
||||
app_model: App,
|
||||
mode: AppMode,
|
||||
site: Site,
|
||||
end_user_id: str | None,
|
||||
features: FeatureModel,
|
||||
@ -109,6 +111,7 @@ class WebAppSiteResponse(ResponseModel):
|
||||
|
||||
return cls(
|
||||
app_id=app_model.id,
|
||||
mode=mode,
|
||||
end_user_id=end_user_id,
|
||||
enable_site=app_model.enable_site,
|
||||
site=site_response,
|
||||
@ -167,6 +170,7 @@ class AppSiteApi(WebApiResource):
|
||||
return WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.value_of(app_model.mode_compatible_with_agent_with_session(session=db.session())),
|
||||
site=site,
|
||||
end_user_id=end_user.id,
|
||||
features=features,
|
||||
|
||||
@ -965,6 +965,12 @@ Returns Server-Sent Events stream.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| tool_icons | object | Tool icon metadata keyed by tool name | No |
|
||||
|
||||
#### AppMode
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| AppMode | string | | |
|
||||
|
||||
#### AppPermissionQuery
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -1646,6 +1652,7 @@ in form definition, or a variable while the workflow is running.
|
||||
| custom_config | [WebAppCustomConfigResponse](#webappcustomconfigresponse) | | No |
|
||||
| enable_site | boolean | | Yes |
|
||||
| end_user_id | string | | No |
|
||||
| mode | [AppMode](#appmode) | | Yes |
|
||||
| model_config | [WebModelConfigResponse](#webmodelconfigresponse) | | No |
|
||||
| plan | string | | Yes |
|
||||
| site | [WebSiteResponse](#websiteresponse) | | Yes |
|
||||
|
||||
@ -97,6 +97,7 @@ class TestAppSiteApi:
|
||||
assert result["end_user_id"] == end_user.id
|
||||
assert result["plan"] == "basic"
|
||||
assert result["enable_site"] is True
|
||||
assert result["mode"] == AppMode.CHAT
|
||||
|
||||
@patch("controllers.web.site.FileService.get_file_presigned_url")
|
||||
@patch("controllers.web.site.FeatureService.get_features")
|
||||
@ -178,6 +179,7 @@ class TestWebAppSiteResponse:
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.CHAT,
|
||||
site=_site_model(app_id=app_model.id),
|
||||
end_user_id="eu-1",
|
||||
features=FeatureModel(can_replace_logo=False, webapp_copyright_enabled=True),
|
||||
@ -185,6 +187,7 @@ class TestWebAppSiteResponse:
|
||||
)
|
||||
|
||||
assert response.app_id == app_model.id
|
||||
assert response.mode == AppMode.CHAT
|
||||
assert response.end_user_id == "eu-1"
|
||||
assert response.enable_site is True
|
||||
assert response.plan == "basic"
|
||||
@ -209,6 +212,7 @@ class TestWebAppSiteResponse:
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.CHAT,
|
||||
site=site,
|
||||
end_user_id=None,
|
||||
features=FeatureModel(can_replace_logo=False, webapp_copyright_enabled=True),
|
||||
@ -236,6 +240,7 @@ class TestWebAppSiteResponse:
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.CHAT,
|
||||
site=_site_model(app_id=app_model.id),
|
||||
end_user_id="eu-1",
|
||||
features=FeatureModel(can_replace_logo=True, webapp_copyright_enabled=True),
|
||||
|
||||
@ -163,6 +163,7 @@ def test_get_form_includes_site(monkeypatch: pytest.MonkeyPatch, app: Flask, dat
|
||||
assert body["expiration_time"] == int(expiration_time.timestamp())
|
||||
assert body["site"] == {
|
||||
"app_id": app_model.id,
|
||||
"mode": "chat",
|
||||
"end_user_id": None,
|
||||
"enable_site": True,
|
||||
"site": {
|
||||
@ -383,6 +384,7 @@ def test_get_form_allows_backstage_token(monkeypatch: pytest.MonkeyPatch, app: F
|
||||
assert body["expiration_time"] == int(expiration_time.timestamp())
|
||||
assert body["site"] == {
|
||||
"app_id": app_model.id,
|
||||
"mode": "chat",
|
||||
"end_user_id": None,
|
||||
"enable_site": True,
|
||||
"site": {
|
||||
|
||||
@ -3,7 +3,42 @@ from unittest.mock import MagicMock, patch
|
||||
from configs import dify_config
|
||||
from controllers.web import site as site_module
|
||||
from extensions.storage.storage_type import StorageType
|
||||
from models.model import IconType, Site
|
||||
from models.model import AppMode, IconType, Site
|
||||
from services.feature_service import FeatureModel
|
||||
|
||||
|
||||
def test_app_site_api_returns_legacy_agent_compatible_mode() -> None:
|
||||
app_model = MagicMock()
|
||||
app_model.id = "app-id"
|
||||
app_model.tenant_id = "tenant-id"
|
||||
app_model.tenant = MagicMock(id="tenant-id", status="normal")
|
||||
app_model.mode_compatible_with_agent_with_session.return_value = AppMode.AGENT_CHAT
|
||||
end_user = MagicMock(id="end-user-id")
|
||||
site = MagicMock(spec=Site)
|
||||
response = MagicMock()
|
||||
response.model_dump.return_value = {"mode": AppMode.AGENT_CHAT}
|
||||
|
||||
with (
|
||||
patch.object(site_module, "db") as mock_db,
|
||||
patch.object(site_module.FeatureService, "get_features", return_value=FeatureModel(can_replace_logo=False)),
|
||||
patch.object(site_module, "_build_site_icon_url", return_value=None),
|
||||
patch.object(site_module.WebAppSiteResponse, "from_app_site", return_value=response) as mock_from_app_site,
|
||||
):
|
||||
mock_db.session.scalar.return_value = site
|
||||
result = site_module.AppSiteApi().get(app_model, end_user)
|
||||
|
||||
assert result["mode"] == AppMode.AGENT_CHAT
|
||||
app_model.mode_compatible_with_agent_with_session.assert_called_once_with(session=mock_db.session())
|
||||
mock_from_app_site.assert_called_once_with(
|
||||
tenant=app_model.tenant,
|
||||
app_model=app_model,
|
||||
mode=AppMode.AGENT_CHAT,
|
||||
site=site,
|
||||
end_user_id=end_user.id,
|
||||
features=FeatureModel(can_replace_logo=False),
|
||||
can_replace_logo=False,
|
||||
icon_url=None,
|
||||
)
|
||||
|
||||
|
||||
def test_build_site_icon_url_uses_s3_presigned_url() -> None:
|
||||
|
||||
@ -43,6 +43,16 @@ export type AppMetaResponse = {
|
||||
}
|
||||
}
|
||||
|
||||
export type AppMode =
|
||||
| 'advanced-chat'
|
||||
| 'agent'
|
||||
| 'agent-chat'
|
||||
| 'channel'
|
||||
| 'chat'
|
||||
| 'completion'
|
||||
| 'rag-pipeline'
|
||||
| 'workflow'
|
||||
|
||||
export type AppPermissionQuery = {
|
||||
appId: string
|
||||
}
|
||||
@ -575,6 +585,7 @@ export type WebAppSiteResponse = {
|
||||
custom_config?: WebAppCustomConfigResponse | null
|
||||
enable_site: boolean
|
||||
end_user_id?: string | null
|
||||
mode: AppMode
|
||||
model_config?: WebModelConfigResponse | null
|
||||
plan: string
|
||||
site: WebSiteResponse
|
||||
|
||||
@ -39,6 +39,20 @@ export const zAppMetaResponse = z.object({
|
||||
tool_icons: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppMode
|
||||
*/
|
||||
export const zAppMode = z.enum([
|
||||
'advanced-chat',
|
||||
'agent',
|
||||
'agent-chat',
|
||||
'channel',
|
||||
'chat',
|
||||
'completion',
|
||||
'rag-pipeline',
|
||||
'workflow',
|
||||
])
|
||||
|
||||
/**
|
||||
* AppPermissionQuery
|
||||
*/
|
||||
@ -884,6 +898,7 @@ export const zWebAppSiteResponse = z.object({
|
||||
custom_config: zWebAppCustomConfigResponse.nullish(),
|
||||
enable_site: z.boolean(),
|
||||
end_user_id: z.string().nullish(),
|
||||
mode: zAppMode,
|
||||
model_config: zWebModelConfigResponse.nullish(),
|
||||
plan: z.string(),
|
||||
site: zWebSiteResponse,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user