mirror of
https://github.com/langgenius/dify.git
synced 2026-06-23 04:11:09 +08:00
fix: require Agent App role (#37601)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
0dd966f7b8
commit
43192036fa
@ -66,14 +66,30 @@ class AgentIdPath(BaseModel):
|
||||
class AgentAppCreatePayload(BaseModel):
|
||||
name: str = Field(..., min_length=1, description="Agent name")
|
||||
description: str | None = Field(default=None, description="Agent description (max 400 chars)", max_length=400)
|
||||
role: str = Field(default="", description="Agent role", max_length=255)
|
||||
role: str = Field(..., min_length=1, description="Agent role", max_length=255)
|
||||
icon_type: IconType | None = Field(default=None, description="Icon type")
|
||||
icon: str | None = Field(default=None, description="Icon")
|
||||
icon_background: str | None = Field(default=None, description="Icon background color")
|
||||
|
||||
@field_validator("role")
|
||||
@classmethod
|
||||
def validate_role(cls, value: str) -> str:
|
||||
role = value.strip()
|
||||
if not role:
|
||||
raise ValueError("Agent role is required.")
|
||||
return role
|
||||
|
||||
|
||||
class AgentAppUpdatePayload(UpdateAppPayload):
|
||||
role: str | None = Field(default=None, description="Agent role", max_length=255)
|
||||
role: str = Field(..., min_length=1, description="Agent role", max_length=255)
|
||||
|
||||
@field_validator("role")
|
||||
@classmethod
|
||||
def validate_role(cls, value: str) -> str:
|
||||
role = value.strip()
|
||||
if not role:
|
||||
raise ValueError("Agent role is required.")
|
||||
return role
|
||||
|
||||
|
||||
class AgentAppPublishedReferenceResponse(BaseModel):
|
||||
|
||||
@ -11376,7 +11376,7 @@ Default namespace
|
||||
| icon_background | string | Icon background color | No |
|
||||
| icon_type | [IconType](#icontype) | Icon type | No |
|
||||
| name | string | Agent name | Yes |
|
||||
| role | string | Agent role | No |
|
||||
| role | string | Agent role | Yes |
|
||||
|
||||
#### AgentAppFeaturesPayload
|
||||
|
||||
@ -11458,7 +11458,7 @@ default (the config form sends the full desired feature state on save).
|
||||
| icon_type | [IconType](#icontype) | Icon type | No |
|
||||
| max_active_requests | integer | Maximum active requests | No |
|
||||
| name | string | App name | Yes |
|
||||
| role | string | Agent role | No |
|
||||
| role | string | Agent role | Yes |
|
||||
| use_icon_as_answer_icon | boolean | Use icon as answer icon | No |
|
||||
|
||||
#### AgentAverageResponseTimeStatisticResponse
|
||||
|
||||
@ -274,7 +274,13 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/agent",
|
||||
json={"name": "Iris", "description": "Agent app", "icon_type": "emoji", "icon": "robot"},
|
||||
json={
|
||||
"name": "Iris",
|
||||
"description": "Agent app",
|
||||
"role": "Coordinator",
|
||||
"icon_type": "emoji",
|
||||
"icon": "robot",
|
||||
},
|
||||
):
|
||||
created, status = unwrap(AgentAppListApi.post)(AgentAppListApi(), "tenant-1", SimpleNamespace(id=account_id))
|
||||
|
||||
@ -287,6 +293,23 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
create_call = cast(dict[str, object], captured["create"])
|
||||
create_params = cast(Any, create_call["params"])
|
||||
assert create_params.mode == "agent"
|
||||
assert create_params.agent_role == "Coordinator"
|
||||
|
||||
|
||||
def test_agent_app_create_requires_role(app: Flask, account_id: str) -> None:
|
||||
with app.test_request_context(
|
||||
"/console/api/agent",
|
||||
json={"name": "Iris", "description": "Agent app", "icon_type": "emoji", "icon": "robot"},
|
||||
):
|
||||
with pytest.raises(ValueError, match="Field required"):
|
||||
unwrap(AgentAppListApi.post)(AgentAppListApi(), "tenant-1", SimpleNamespace(id=account_id))
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/agent",
|
||||
json={"name": "Iris", "description": "Agent app", "role": " ", "icon_type": "emoji", "icon": "robot"},
|
||||
):
|
||||
with pytest.raises(ValueError, match="Agent role is required"):
|
||||
unwrap(AgentAppListApi.post)(AgentAppListApi(), "tenant-1", SimpleNamespace(id=account_id))
|
||||
|
||||
|
||||
def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
@ -335,7 +358,7 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/agent/00000000-0000-0000-0000-000000000001",
|
||||
json={"name": "Renamed", "description": "", "icon_type": "emoji", "icon": "R"},
|
||||
json={"name": "Renamed", "description": "", "role": "Reviewer", "icon_type": "emoji", "icon": "R"},
|
||||
):
|
||||
updated = unwrap(AgentAppApi.put)(AgentAppApi(), "tenant-1", agent_id)
|
||||
|
||||
@ -347,6 +370,7 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
assert "bound_agent_id" not in updated
|
||||
update_call = cast(dict[str, object], captured["update"])
|
||||
assert update_call["app"] is app_model
|
||||
assert cast(dict[str, object], update_call["args"])["role"] == "Reviewer"
|
||||
|
||||
deleted, status = unwrap(AgentAppApi.delete)(AgentAppApi(), "tenant-1", agent_id)
|
||||
assert (deleted, status) == ("", 204)
|
||||
@ -399,6 +423,45 @@ def test_agent_app_copy_uses_agent_id_and_returns_agent_detail(
|
||||
}
|
||||
|
||||
|
||||
def test_agent_app_update_rejects_empty_role(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
agent_id = "00000000-0000-0000-0000-000000000001"
|
||||
app_model = _app_detail_obj(id="app-1", bound_agent_id=agent_id)
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService,
|
||||
"get_agent_app_model",
|
||||
lambda _self, **kwargs: app_model,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService,
|
||||
"get_app_backing_agent",
|
||||
lambda _self, **kwargs: SimpleNamespace(id=agent_id, role="", active_config_snapshot_id=None),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.FeatureService,
|
||||
"get_system_features",
|
||||
lambda: SimpleNamespace(webapp_auth=SimpleNamespace(enabled=False)),
|
||||
)
|
||||
|
||||
class FakeAppService:
|
||||
def get_app(self, app_obj: object) -> object:
|
||||
return app_obj
|
||||
|
||||
def update_app(self, app_obj: object, args: dict[str, object]) -> object:
|
||||
captured["update"] = {"app": app_obj, "args": args}
|
||||
return _app_detail_obj(id="app-1", name=args["name"], bound_agent_id=agent_id)
|
||||
|
||||
monkeypatch.setattr(roster_controller, "AppService", FakeAppService)
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/agent/00000000-0000-0000-0000-000000000001",
|
||||
json={"name": "Renamed", "description": "", "role": "", "icon_type": "emoji", "icon": "R"},
|
||||
):
|
||||
with pytest.raises(ValueError, match="String should have at least 1 character"):
|
||||
unwrap(AgentAppApi.put)(AgentAppApi(), "tenant-1", agent_id)
|
||||
|
||||
|
||||
def test_invite_options_get_parses_app_id(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ export type AgentAppCreatePayload = {
|
||||
icon_background?: string | null
|
||||
icon_type?: IconType | null
|
||||
name: string
|
||||
role?: string
|
||||
role: string
|
||||
}
|
||||
|
||||
export type AppDetailWithSite = {
|
||||
@ -67,7 +67,7 @@ export type AgentAppUpdatePayload = {
|
||||
icon_type?: IconType | null
|
||||
max_active_requests?: number | null
|
||||
name: string
|
||||
role?: string | null
|
||||
role: string
|
||||
use_icon_as_answer_icon?: boolean | null
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ export const zAgentAppCreatePayload = z.object({
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: zIconType.nullish(),
|
||||
name: z.string().min(1),
|
||||
role: z.string().max(255).optional().default(''),
|
||||
role: z.string().min(1).max(255),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -105,7 +105,7 @@ export const zAgentAppUpdatePayload = z.object({
|
||||
icon_type: zIconType.nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
name: z.string().min(1),
|
||||
role: z.string().max(255).nullish(),
|
||||
role: z.string().min(1).max(255),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
})
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user