mirror of
https://github.com/langgenius/dify.git
synced 2026-06-17 06:21:07 +08:00
fix: issue (#37508)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
f7c90d4873
commit
dcc0b95e11
@ -53,14 +53,20 @@ 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)
|
||||
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")
|
||||
|
||||
|
||||
class AgentAppUpdatePayload(UpdateAppPayload):
|
||||
role: str | None = Field(default=None, description="Agent role", max_length=255)
|
||||
|
||||
|
||||
register_schema_models(
|
||||
console_ns,
|
||||
AgentAppCreatePayload,
|
||||
AgentAppUpdatePayload,
|
||||
AgentInviteOptionsQuery,
|
||||
AgentIdPath,
|
||||
AppListQuery,
|
||||
@ -89,20 +95,32 @@ def _serialize_agent_app_detail(app_model) -> dict:
|
||||
app_setting = EnterpriseService.WebAppAuth.get_app_access_mode_by_id(app_id=str(app_model.id))
|
||||
app_model.access_mode = app_setting.access_mode # type: ignore[attr-defined]
|
||||
|
||||
payload = AppDetailWithSite.model_validate(app_model, from_attributes=True).model_dump(mode="json")
|
||||
agent_id = payload.pop("bound_agent_id", None)
|
||||
if not agent_id:
|
||||
agent = _agent_roster_service().get_app_backing_agent(tenant_id=app_model.tenant_id, app_id=app_model.id)
|
||||
if not agent:
|
||||
raise AgentNotFoundError()
|
||||
payload["id"] = agent_id
|
||||
payload = AppDetailWithSite.model_validate(app_model, from_attributes=True).model_dump(mode="json")
|
||||
payload.pop("bound_agent_id", None)
|
||||
payload["app_id"] = str(app_model.id)
|
||||
payload["id"] = agent.id
|
||||
payload["role"] = agent.role or ""
|
||||
return payload
|
||||
|
||||
|
||||
def _serialize_agent_app_pagination(app_pagination) -> dict:
|
||||
def _serialize_agent_app_pagination(app_pagination, *, tenant_id: str) -> dict:
|
||||
app_ids = [str(app.id) for app in app_pagination.items]
|
||||
agents_by_app_id = _agent_roster_service().load_app_backing_agents_by_app_id(
|
||||
tenant_id=tenant_id,
|
||||
app_ids=app_ids,
|
||||
)
|
||||
payload = AppPagination.model_validate(app_pagination, from_attributes=True).model_dump(mode="json")
|
||||
for item in payload["data"]:
|
||||
agent_id = item.pop("bound_agent_id", None)
|
||||
if agent_id:
|
||||
item["id"] = agent_id
|
||||
app_id = item["id"]
|
||||
item.pop("bound_agent_id", None)
|
||||
agent = agents_by_app_id.get(app_id)
|
||||
if agent:
|
||||
item["app_id"] = app_id
|
||||
item["id"] = agent.id
|
||||
item["role"] = agent.role or ""
|
||||
return payload
|
||||
|
||||
|
||||
@ -137,7 +155,7 @@ class AgentAppListApi(Resource):
|
||||
empty = AppPagination(page=args.page, limit=args.limit, total=0, has_more=False, data=[])
|
||||
return empty.model_dump(mode="json")
|
||||
|
||||
return _serialize_agent_app_pagination(app_pagination)
|
||||
return _serialize_agent_app_pagination(app_pagination, tenant_id=current_tenant_id)
|
||||
|
||||
@console_ns.expect(console_ns.models[AgentAppCreatePayload.__name__])
|
||||
@console_ns.response(201, "Agent app created successfully", console_ns.models[AppDetailWithSite.__name__])
|
||||
@ -156,6 +174,7 @@ class AgentAppListApi(Resource):
|
||||
name=args.name,
|
||||
description=args.description,
|
||||
mode="agent",
|
||||
agent_role=args.role,
|
||||
icon_type=args.icon_type,
|
||||
icon=args.icon,
|
||||
icon_background=args.icon_background,
|
||||
@ -177,7 +196,7 @@ class AgentAppApi(Resource):
|
||||
app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id)
|
||||
return _serialize_agent_app_detail(app_model)
|
||||
|
||||
@console_ns.expect(console_ns.models[UpdateAppPayload.__name__])
|
||||
@console_ns.expect(console_ns.models[AgentAppUpdatePayload.__name__])
|
||||
@console_ns.response(200, "Agent app updated successfully", console_ns.models[AppDetailWithSite.__name__])
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@console_ns.response(400, "Invalid request parameters")
|
||||
@ -188,7 +207,7 @@ class AgentAppApi(Resource):
|
||||
@with_current_tenant_id
|
||||
def put(self, tenant_id: str, agent_id: UUID):
|
||||
app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id)
|
||||
args = UpdateAppPayload.model_validate(console_ns.payload)
|
||||
args = AgentAppUpdatePayload.model_validate(console_ns.payload)
|
||||
args_dict: AppService.ArgsDict = {
|
||||
"name": args.name,
|
||||
"description": args.description or "",
|
||||
@ -197,6 +216,7 @@ class AgentAppApi(Resource):
|
||||
"icon_background": args.icon_background or "",
|
||||
"use_icon_as_answer_icon": args.use_icon_as_answer_icon or False,
|
||||
"max_active_requests": args.max_active_requests or 0,
|
||||
"role": args.role,
|
||||
}
|
||||
updated = AppService().update_app(app_model, args_dict)
|
||||
return _serialize_agent_app_detail(updated)
|
||||
|
||||
@ -400,6 +400,9 @@ class AppPartial(ResponseModel):
|
||||
has_draft_trigger: bool | None = None
|
||||
# For Agent App type: the roster Agent backing this app (None otherwise).
|
||||
bound_agent_id: str | None = None
|
||||
# For Agent App responses exposed through /agent.
|
||||
app_id: str | None = None
|
||||
role: str | None = None
|
||||
is_starred: bool = False
|
||||
|
||||
@computed_field(return_type=str | None) # type: ignore
|
||||
@ -451,6 +454,9 @@ class AppDetailWithSite(AppDetail):
|
||||
site: Site | None = None
|
||||
# For Agent App type: the roster Agent backing this app (None otherwise).
|
||||
bound_agent_id: str | None = None
|
||||
# For Agent App responses exposed through /agent.
|
||||
app_id: str | None = None
|
||||
role: str | None = None
|
||||
|
||||
@computed_field(return_type=str | None) # type: ignore
|
||||
@property
|
||||
|
||||
@ -382,7 +382,7 @@ Check if activation token is valid
|
||||
|
||||
| Required | Schema |
|
||||
| -------- | ------ |
|
||||
| Yes | **application/json**: [UpdateAppPayload](#updateapppayload)<br> |
|
||||
| Yes | **application/json**: [AgentAppUpdatePayload](#agentappupdatepayload)<br> |
|
||||
|
||||
#### Responses
|
||||
|
||||
@ -11286,6 +11286,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 |
|
||||
|
||||
#### AgentAppFeaturesPayload
|
||||
|
||||
@ -11304,6 +11305,19 @@ default (the config form sends the full desired feature state on save).
|
||||
| suggested_questions_after_answer | [AgentSuggestedQuestionsAfterAnswerFeatureConfig](#agentsuggestedquestionsafteranswerfeatureconfig) | Follow-up suggestions config, e.g. {'enabled': true} | No |
|
||||
| text_to_speech | [AgentTextToSpeechFeatureConfig](#agenttexttospeechfeatureconfig) | Text-to-speech config | No |
|
||||
|
||||
#### AgentAppUpdatePayload
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| description | string | App description (max 400 chars) | No |
|
||||
| icon | string | Icon | No |
|
||||
| icon_background | string | Icon background color | No |
|
||||
| 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 |
|
||||
| use_icon_as_answer_icon | boolean | Use icon as answer icon | No |
|
||||
|
||||
#### AgentCliToolAuthorizationStatus
|
||||
|
||||
Authorization state for Agent-scoped CLI tools.
|
||||
@ -12502,6 +12516,7 @@ Enum class for api provider schema type.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| access_mode | string | | No |
|
||||
| api_base_url | string | | No |
|
||||
| app_id | string | | No |
|
||||
| bound_agent_id | string | | No |
|
||||
| created_at | integer | | No |
|
||||
| created_by | string | | No |
|
||||
@ -12518,6 +12533,7 @@ Enum class for api provider schema type.
|
||||
| mode | string | | Yes |
|
||||
| model_config | [ModelConfig](#modelconfig) | | No |
|
||||
| name | string | | Yes |
|
||||
| role | string | | No |
|
||||
| site | [Site](#site) | | No |
|
||||
| tags | [ [Tag](#tag) ] | | No |
|
||||
| tracing | [JSONValue](#jsonvalue) | | No |
|
||||
@ -12622,6 +12638,7 @@ AppMCPServer Status Enum
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| access_mode | string | | No |
|
||||
| app_id | string | | No |
|
||||
| author_name | string | | No |
|
||||
| bound_agent_id | string | | No |
|
||||
| create_user_name | string | | No |
|
||||
@ -12639,6 +12656,7 @@ AppMCPServer Status Enum
|
||||
| mode | string | | Yes |
|
||||
| model_config | [ModelConfigPartial](#modelconfigpartial) | | No |
|
||||
| name | string | | Yes |
|
||||
| role | string | | No |
|
||||
| tags | [ [Tag](#tag) ] | | No |
|
||||
| updated_at | integer | | No |
|
||||
| updated_by | string | | No |
|
||||
|
||||
@ -282,6 +282,7 @@ class AgentRosterService:
|
||||
app_id: str,
|
||||
name: str,
|
||||
description: str = "",
|
||||
role: str = "",
|
||||
icon_type: Any = None,
|
||||
icon: str | None = None,
|
||||
icon_background: str | None = None,
|
||||
@ -298,7 +299,7 @@ class AgentRosterService:
|
||||
tenant_id=tenant_id,
|
||||
name=name,
|
||||
description=description,
|
||||
role="",
|
||||
role=role,
|
||||
icon_type=icon_type,
|
||||
icon=icon,
|
||||
icon_background=icon_background,
|
||||
@ -341,6 +342,21 @@ class AgentRosterService:
|
||||
self._session.flush()
|
||||
return agent
|
||||
|
||||
def load_app_backing_agents_by_app_id(self, *, tenant_id: str, app_ids: list[str]) -> dict[str, Agent]:
|
||||
"""Return active app-backed Agents keyed by Agent App id."""
|
||||
if not app_ids:
|
||||
return {}
|
||||
agents = self._session.scalars(
|
||||
select(Agent).where(
|
||||
Agent.tenant_id == tenant_id,
|
||||
Agent.app_id.in_(app_ids),
|
||||
Agent.scope == AgentScope.ROSTER,
|
||||
Agent.source == AgentSource.AGENT_APP,
|
||||
Agent.status == AgentStatus.ACTIVE,
|
||||
)
|
||||
).all()
|
||||
return {agent.app_id: agent for agent in agents if agent.app_id}
|
||||
|
||||
def get_app_backing_agent(self, *, tenant_id: str, app_id: str) -> Agent | None:
|
||||
"""Return the roster Agent that backs the given Agent App, if any."""
|
||||
return self._session.scalar(
|
||||
@ -444,12 +460,36 @@ class AgentRosterService:
|
||||
agent.updated_by = account_id
|
||||
self._session.commit()
|
||||
|
||||
@staticmethod
|
||||
def _visible_version_operations(agent: Agent) -> set[AgentConfigRevisionOperation]:
|
||||
if agent.source == AgentSource.AGENT_APP:
|
||||
return {AgentConfigRevisionOperation.SAVE_NEW_VERSION}
|
||||
return {
|
||||
AgentConfigRevisionOperation.CREATE_VERSION,
|
||||
AgentConfigRevisionOperation.SAVE_NEW_VERSION,
|
||||
AgentConfigRevisionOperation.SAVE_NEW_AGENT,
|
||||
AgentConfigRevisionOperation.SAVE_TO_ROSTER,
|
||||
}
|
||||
|
||||
def list_agent_versions(self, *, tenant_id: str, agent_id: str) -> list[dict[str, Any]]:
|
||||
self._get_agent(tenant_id=tenant_id, agent_id=agent_id, roster_only=True)
|
||||
agent = self._get_agent(tenant_id=tenant_id, agent_id=agent_id, roster_only=True)
|
||||
visible_version_ids = (
|
||||
select(AgentConfigRevision.current_snapshot_id)
|
||||
.where(
|
||||
AgentConfigRevision.tenant_id == tenant_id,
|
||||
AgentConfigRevision.agent_id == agent_id,
|
||||
AgentConfigRevision.operation.in_(self._visible_version_operations(agent)),
|
||||
)
|
||||
.subquery()
|
||||
)
|
||||
versions = list(
|
||||
self._session.scalars(
|
||||
select(AgentConfigSnapshot)
|
||||
.where(AgentConfigSnapshot.tenant_id == tenant_id, AgentConfigSnapshot.agent_id == agent_id)
|
||||
.where(
|
||||
AgentConfigSnapshot.tenant_id == tenant_id,
|
||||
AgentConfigSnapshot.agent_id == agent_id,
|
||||
AgentConfigSnapshot.id.in_(select(visible_version_ids.c.current_snapshot_id)),
|
||||
)
|
||||
.order_by(AgentConfigSnapshot.version.desc())
|
||||
).all()
|
||||
)
|
||||
@ -460,7 +500,19 @@ class AgentRosterService:
|
||||
]
|
||||
|
||||
def get_agent_version_detail(self, *, tenant_id: str, agent_id: str, version_id: str) -> dict[str, Any]:
|
||||
self._get_agent(tenant_id=tenant_id, agent_id=agent_id, roster_only=True)
|
||||
agent = self._get_agent(tenant_id=tenant_id, agent_id=agent_id, roster_only=True)
|
||||
visible_revision_id = self._session.scalar(
|
||||
select(AgentConfigRevision.id)
|
||||
.where(
|
||||
AgentConfigRevision.tenant_id == tenant_id,
|
||||
AgentConfigRevision.agent_id == agent_id,
|
||||
AgentConfigRevision.current_snapshot_id == version_id,
|
||||
AgentConfigRevision.operation.in_(self._visible_version_operations(agent)),
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
if not visible_revision_id:
|
||||
raise AgentVersionNotFoundError()
|
||||
version = self._get_version(tenant_id=tenant_id, agent_id=agent_id, version_id=version_id)
|
||||
revisions = list(
|
||||
self._session.scalars(
|
||||
|
||||
@ -2,7 +2,7 @@ import json
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import Any, Literal, TypedDict, cast, override
|
||||
from typing import Any, Literal, NotRequired, TypedDict, cast, override
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask_sqlalchemy.pagination import Pagination
|
||||
@ -63,6 +63,7 @@ class CreateAppParams(BaseModel):
|
||||
name: str = Field(min_length=1)
|
||||
description: str | None = None
|
||||
mode: Literal["chat", "agent-chat", "agent", "advanced-chat", "workflow", "completion"]
|
||||
agent_role: str = Field(default="", max_length=255)
|
||||
icon_type: str | None = None
|
||||
icon: str | None = None
|
||||
icon_background: str | None = None
|
||||
@ -90,6 +91,8 @@ class AppService:
|
||||
filters.append(App.mode == AppMode.AGENT_CHAT)
|
||||
elif params.mode == "agent":
|
||||
filters.append(App.mode == AppMode.AGENT)
|
||||
elif params.mode == "all":
|
||||
filters.append(App.mode != AppMode.AGENT)
|
||||
|
||||
if isinstance(params, AppListParams):
|
||||
if params.status:
|
||||
@ -412,6 +415,7 @@ class AppService:
|
||||
app_id=app.id,
|
||||
name=params.name,
|
||||
description=params.description or "",
|
||||
role=params.agent_role,
|
||||
icon_type=icon_type,
|
||||
icon=params.icon,
|
||||
icon_background=params.icon_background,
|
||||
@ -507,6 +511,7 @@ class AppService:
|
||||
icon_background: str
|
||||
use_icon_as_answer_icon: bool
|
||||
max_active_requests: int
|
||||
role: NotRequired[str | None]
|
||||
|
||||
@staticmethod
|
||||
def _get_backing_agent_for_update(app: App) -> Agent | None:
|
||||
@ -538,6 +543,7 @@ class AppService:
|
||||
icon_type: IconType | str | None = None,
|
||||
icon: str | None = None,
|
||||
icon_background: str | None = None,
|
||||
role: str | None = None,
|
||||
account_id: str | None = None,
|
||||
updated_at: datetime | None = None,
|
||||
) -> None:
|
||||
@ -560,6 +566,8 @@ class AppService:
|
||||
agent.icon = icon
|
||||
if icon_background is not None:
|
||||
agent.icon_background = icon_background
|
||||
if role is not None:
|
||||
agent.role = role
|
||||
agent.updated_by = account_id
|
||||
if updated_at is not None:
|
||||
agent.updated_at = updated_at
|
||||
@ -594,6 +602,7 @@ class AppService:
|
||||
icon_type=app.icon_type,
|
||||
icon=app.icon,
|
||||
icon_background=app.icon_background,
|
||||
role=args.get("role"),
|
||||
account_id=current_user.id,
|
||||
updated_at=app.updated_at,
|
||||
)
|
||||
|
||||
@ -113,6 +113,7 @@ def _app_detail_obj(**overrides):
|
||||
"deleted_tools": [],
|
||||
"site": None,
|
||||
"bound_agent_id": "00000000-0000-0000-0000-000000000001",
|
||||
"tenant_id": "tenant-1",
|
||||
}
|
||||
data.update(overrides)
|
||||
return SimpleNamespace(**data)
|
||||
@ -195,6 +196,16 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
return _app_detail_obj(id="app-created", bound_agent_id="agent-created")
|
||||
|
||||
monkeypatch.setattr(roster_controller, "AppService", FakeAppService)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService,
|
||||
"load_app_backing_agents_by_app_id",
|
||||
lambda _self, **kwargs: {"app-list": SimpleNamespace(id="agent-list", role="List role")},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.AgentRosterService,
|
||||
"get_app_backing_agent",
|
||||
lambda _self, **kwargs: SimpleNamespace(id="agent-created", role="Created role"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.FeatureService,
|
||||
"get_system_features",
|
||||
@ -208,6 +219,8 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
assert listed["limit"] == 10
|
||||
assert listed["total"] == 1
|
||||
assert listed["data"][0]["id"] == "agent-list"
|
||||
assert listed["data"][0]["app_id"] == "app-list"
|
||||
assert listed["data"][0]["role"] == "List role"
|
||||
assert "bound_agent_id" not in listed["data"][0]
|
||||
list_call = cast(dict[str, object], captured["list"])
|
||||
list_params = cast(Any, list_call["params"])
|
||||
@ -222,6 +235,8 @@ def test_agent_app_list_and_create_use_agent_route(
|
||||
|
||||
assert status == 201
|
||||
assert created["id"] == "agent-created"
|
||||
assert created["app_id"] == "app-created"
|
||||
assert created["role"] == "Created role"
|
||||
assert "bound_agent_id" not in created
|
||||
create_call = cast(dict[str, object], captured["create"])
|
||||
create_params = cast(Any, create_call["params"])
|
||||
@ -240,6 +255,11 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
"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="Resolved role"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
roster_controller.FeatureService,
|
||||
"get_system_features",
|
||||
@ -262,6 +282,8 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
|
||||
detail = unwrap(AgentAppApi.get)(AgentAppApi(), "tenant-1", agent_id)
|
||||
assert detail["id"] == agent_id
|
||||
assert detail["app_id"] == "app-1"
|
||||
assert detail["role"] == "Resolved role"
|
||||
assert "bound_agent_id" not in detail
|
||||
|
||||
with app.test_request_context(
|
||||
@ -272,6 +294,8 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
|
||||
|
||||
assert updated["name"] == "Renamed"
|
||||
assert updated["id"] == agent_id
|
||||
assert updated["app_id"] == "app-1"
|
||||
assert updated["role"] == "Resolved role"
|
||||
assert "bound_agent_id" not in updated
|
||||
update_call = cast(dict[str, object], captured["update"])
|
||||
assert update_call["app"] is app_model
|
||||
|
||||
@ -29,6 +29,7 @@ from services.agent.composer_validator import ComposerConfigValidator
|
||||
from services.agent.errors import InvalidComposerConfigError
|
||||
from services.agent.roster_service import AgentRosterService
|
||||
from services.agent.workflow_publish_service import WorkflowAgentPublishService
|
||||
from services.app_service import AppListParams, AppService
|
||||
from services.entities.agent_entities import AgentSoulConfig, ComposerSavePayload, ComposerSaveStrategy, ComposerVariant
|
||||
|
||||
|
||||
@ -769,7 +770,7 @@ def test_roster_update_archive_versions_and_detail(monkeypatch):
|
||||
created_by="account-1",
|
||||
created_at=revision_created_at,
|
||||
)
|
||||
fake_session = FakeSession(scalars=[[listed_version], [revision]])
|
||||
fake_session = FakeSession(scalar=["visible-revision"], scalars=[[listed_version], [revision]])
|
||||
agent = Agent(
|
||||
id="agent-1",
|
||||
tenant_id="tenant-1",
|
||||
@ -825,6 +826,7 @@ def test_roster_create_detail_and_lookup_helpers(monkeypatch):
|
||||
payload = roster_service.RosterAgentCreatePayload(
|
||||
name="Analyst",
|
||||
description="desc",
|
||||
role="Research assistant",
|
||||
icon_type="emoji",
|
||||
icon="A",
|
||||
icon_background="#fff",
|
||||
@ -838,6 +840,7 @@ def test_roster_create_detail_and_lookup_helpers(monkeypatch):
|
||||
account_id="account-1",
|
||||
app_id="app-1",
|
||||
name="Backing Agent",
|
||||
role="Support agent",
|
||||
)
|
||||
found_agent = service._get_agent(tenant_id="tenant-1", agent_id="agent-1")
|
||||
with pytest.raises(roster_service.AgentNotFoundError):
|
||||
@ -849,9 +852,11 @@ def test_roster_create_detail_and_lookup_helpers(monkeypatch):
|
||||
assert service._load_versions_by_id([]) == {}
|
||||
|
||||
assert created.name == "Analyst"
|
||||
assert created.role == "Research assistant"
|
||||
assert created.source == AgentSource.ROSTER
|
||||
assert created.active_config_snapshot_id is not None
|
||||
assert created.active_config_has_model is False
|
||||
assert backing_agent.role == "Support agent"
|
||||
assert backing_agent.active_config_snapshot_id is not None
|
||||
assert backing_agent.active_config_has_model is False
|
||||
assert found_agent.id == "agent-1"
|
||||
@ -859,6 +864,30 @@ def test_roster_create_detail_and_lookup_helpers(monkeypatch):
|
||||
assert loaded_versions["version-1"].agent_id == "agent-1"
|
||||
|
||||
|
||||
def test_agent_app_visible_versions_exclude_draft_saves():
|
||||
agent_app = Agent(source=AgentSource.AGENT_APP)
|
||||
roster_agent = Agent(source=AgentSource.ROSTER)
|
||||
|
||||
agent_app_operations = AgentRosterService._visible_version_operations(agent_app)
|
||||
roster_operations = AgentRosterService._visible_version_operations(roster_agent)
|
||||
|
||||
assert agent_app_operations == {AgentConfigRevisionOperation.SAVE_NEW_VERSION}
|
||||
assert AgentConfigRevisionOperation.SAVE_CURRENT_VERSION not in agent_app_operations
|
||||
assert AgentConfigRevisionOperation.CREATE_VERSION in roster_operations
|
||||
assert AgentConfigRevisionOperation.SAVE_CURRENT_VERSION not in roster_operations
|
||||
|
||||
|
||||
def test_app_list_all_excludes_agent_apps_by_default():
|
||||
filters = AppService._build_app_list_filters(
|
||||
"account-1",
|
||||
"tenant-1",
|
||||
AppListParams(mode="all"),
|
||||
)
|
||||
sql = " ".join(str(filter_) for filter_ in filters)
|
||||
|
||||
assert "apps.mode != :mode_1" in sql
|
||||
|
||||
|
||||
def test_validator_dict_helpers_wrap_validation_errors():
|
||||
valid_soul = ComposerConfigValidator.validate_agent_soul_dict({"prompt": {"system_prompt": "x"}})
|
||||
valid_node_job = ComposerConfigValidator.validate_node_job_dict({"workflow_prompt": "x"})
|
||||
|
||||
@ -18,11 +18,13 @@ export type AgentAppCreatePayload = {
|
||||
icon_background?: string | null
|
||||
icon_type?: IconType | null
|
||||
name: string
|
||||
role?: string
|
||||
}
|
||||
|
||||
export type AppDetailWithSite = {
|
||||
access_mode?: string | null
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
bound_agent_id?: string | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
@ -39,6 +41,7 @@ export type AppDetailWithSite = {
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
name: string
|
||||
role?: string | null
|
||||
site?: Site | null
|
||||
tags?: Array<Tag>
|
||||
tracing?: JsonValue | null
|
||||
@ -56,13 +59,14 @@ export type AgentInviteOptionsResponse = {
|
||||
total: number
|
||||
}
|
||||
|
||||
export type UpdateAppPayload = {
|
||||
export type AgentAppUpdatePayload = {
|
||||
description?: string | null
|
||||
icon?: string | null
|
||||
icon_background?: string | null
|
||||
icon_type?: IconType | null
|
||||
max_active_requests?: number | null
|
||||
name: string
|
||||
role?: string | null
|
||||
use_icon_as_answer_icon?: boolean | null
|
||||
}
|
||||
|
||||
@ -255,6 +259,7 @@ export type AgentConfigSnapshotDetailResponse = {
|
||||
|
||||
export type AppPartial = {
|
||||
access_mode?: string | null
|
||||
app_id?: string | null
|
||||
author_name?: string | null
|
||||
bound_agent_id?: string | null
|
||||
create_user_name?: string | null
|
||||
@ -272,6 +277,7 @@ export type AppPartial = {
|
||||
mode: string
|
||||
model_config?: ModelConfigPartial | null
|
||||
name: string
|
||||
role?: string | null
|
||||
tags?: Array<Tag>
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
@ -1219,6 +1225,7 @@ export type AppPaginationWritable = {
|
||||
export type AppDetailWithSiteWritable = {
|
||||
access_mode?: string | null
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
bound_agent_id?: string | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
@ -1234,6 +1241,7 @@ export type AppDetailWithSiteWritable = {
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
name: string
|
||||
role?: string | null
|
||||
site?: SiteWritable | null
|
||||
tags?: Array<Tag>
|
||||
tracing?: JsonValue | null
|
||||
@ -1245,6 +1253,7 @@ export type AppDetailWithSiteWritable = {
|
||||
|
||||
export type AppPartialWritable = {
|
||||
access_mode?: string | null
|
||||
app_id?: string | null
|
||||
author_name?: string | null
|
||||
bound_agent_id?: string | null
|
||||
create_user_name?: string | null
|
||||
@ -1261,6 +1270,7 @@ export type AppPartialWritable = {
|
||||
mode: string
|
||||
model_config?: ModelConfigPartial | null
|
||||
name: string
|
||||
role?: string | null
|
||||
tags?: Array<Tag>
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
@ -1387,7 +1397,7 @@ export type GetAgentByAgentIdResponses = {
|
||||
export type GetAgentByAgentIdResponse = GetAgentByAgentIdResponses[keyof GetAgentByAgentIdResponses]
|
||||
|
||||
export type PutAgentByAgentIdData = {
|
||||
body: UpdateAppPayload
|
||||
body: AgentAppUpdatePayload
|
||||
path: {
|
||||
agent_id: string
|
||||
}
|
||||
|
||||
@ -92,18 +92,20 @@ 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(''),
|
||||
})
|
||||
|
||||
/**
|
||||
* UpdateAppPayload
|
||||
* AgentAppUpdatePayload
|
||||
*/
|
||||
export const zUpdateAppPayload = z.object({
|
||||
export const zAgentAppUpdatePayload = z.object({
|
||||
description: z.string().max(400).nullish(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: zIconType.nullish(),
|
||||
max_active_requests: z.int().nullish(),
|
||||
name: z.string().min(1),
|
||||
role: z.string().max(255).nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
})
|
||||
|
||||
@ -473,6 +475,7 @@ export const zModelConfigPartial = z.object({
|
||||
*/
|
||||
export const zAppPartial = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
author_name: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
create_user_name: z.string().nullish(),
|
||||
@ -490,6 +493,7 @@ export const zAppPartial = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfigPartial.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
@ -531,6 +535,7 @@ export const zModelConfig = z.object({
|
||||
export const zAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
@ -547,6 +552,7 @@ export const zAppDetailWithSite = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
site: zSite.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: zJsonValue.nullish(),
|
||||
@ -1729,6 +1735,7 @@ export const zMessageInfiniteScrollPaginationResponse = z.object({
|
||||
*/
|
||||
export const zAppPartialWritable = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
author_name: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
create_user_name: z.string().nullish(),
|
||||
@ -1745,6 +1752,7 @@ export const zAppPartialWritable = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfigPartial.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
@ -1788,6 +1796,7 @@ export const zSiteWritable = z.object({
|
||||
export const zAppDetailWithSiteWritable = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
@ -1803,6 +1812,7 @@ export const zAppDetailWithSiteWritable = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
site: zSiteWritable.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: zJsonValue.nullish(),
|
||||
@ -1880,7 +1890,7 @@ export const zGetAgentByAgentIdPath = z.object({
|
||||
*/
|
||||
export const zGetAgentByAgentIdResponse = zAppDetailWithSite
|
||||
|
||||
export const zPutAgentByAgentIdBody = zUpdateAppPayload
|
||||
export const zPutAgentByAgentIdBody = zAgentAppUpdatePayload
|
||||
|
||||
export const zPutAgentByAgentIdPath = z.object({
|
||||
agent_id: z.string(),
|
||||
|
||||
@ -24,6 +24,7 @@ export type CreateAppPayload = {
|
||||
export type AppDetailWithSite = {
|
||||
access_mode?: string | null
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
bound_agent_id?: string | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
@ -40,6 +41,7 @@ export type AppDetailWithSite = {
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
name: string
|
||||
role?: string | null
|
||||
site?: Site | null
|
||||
tags?: Array<Tag>
|
||||
tracing?: JsonValue | null
|
||||
@ -1153,6 +1155,7 @@ export type ApiKeyItem = {
|
||||
|
||||
export type AppPartial = {
|
||||
access_mode?: string | null
|
||||
app_id?: string | null
|
||||
author_name?: string | null
|
||||
bound_agent_id?: string | null
|
||||
create_user_name?: string | null
|
||||
@ -1170,6 +1173,7 @@ export type AppPartial = {
|
||||
mode: string
|
||||
model_config?: ModelConfigPartial | null
|
||||
name: string
|
||||
role?: string | null
|
||||
tags?: Array<Tag>
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
@ -2540,6 +2544,7 @@ export type AppPaginationWritable = {
|
||||
export type AppDetailWithSiteWritable = {
|
||||
access_mode?: string | null
|
||||
api_base_url?: string | null
|
||||
app_id?: string | null
|
||||
bound_agent_id?: string | null
|
||||
created_at?: number | null
|
||||
created_by?: string | null
|
||||
@ -2555,6 +2560,7 @@ export type AppDetailWithSiteWritable = {
|
||||
mode: string
|
||||
model_config?: ModelConfig | null
|
||||
name: string
|
||||
role?: string | null
|
||||
site?: SiteWritable | null
|
||||
tags?: Array<Tag>
|
||||
tracing?: JsonValue | null
|
||||
@ -2589,6 +2595,7 @@ export type WorkflowCommentDetailWritable = {
|
||||
|
||||
export type AppPartialWritable = {
|
||||
access_mode?: string | null
|
||||
app_id?: string | null
|
||||
author_name?: string | null
|
||||
bound_agent_id?: string | null
|
||||
create_user_name?: string | null
|
||||
@ -2605,6 +2612,7 @@ export type AppPartialWritable = {
|
||||
mode: string
|
||||
model_config?: ModelConfigPartial | null
|
||||
name: string
|
||||
role?: string | null
|
||||
tags?: Array<Tag>
|
||||
updated_at?: number | null
|
||||
updated_by?: string | null
|
||||
|
||||
@ -1946,6 +1946,7 @@ export const zModelConfigPartial = z.object({
|
||||
*/
|
||||
export const zAppPartial = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
author_name: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
create_user_name: z.string().nullish(),
|
||||
@ -1963,6 +1964,7 @@ export const zAppPartial = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfigPartial.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
@ -2004,6 +2006,7 @@ export const zModelConfig = z.object({
|
||||
export const zAppDetailWithSite = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
@ -2020,6 +2023,7 @@ export const zAppDetailWithSite = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
site: zSite.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: zJsonValue.nullish(),
|
||||
@ -3433,6 +3437,7 @@ export const zGeneratedAppResponseWritable = zJsonValue
|
||||
*/
|
||||
export const zAppPartialWritable = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
author_name: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
create_user_name: z.string().nullish(),
|
||||
@ -3449,6 +3454,7 @@ export const zAppPartialWritable = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfigPartial.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
updated_at: z.int().nullish(),
|
||||
updated_by: z.string().nullish(),
|
||||
@ -3492,6 +3498,7 @@ export const zSiteWritable = z.object({
|
||||
export const zAppDetailWithSiteWritable = z.object({
|
||||
access_mode: z.string().nullish(),
|
||||
api_base_url: z.string().nullish(),
|
||||
app_id: z.string().nullish(),
|
||||
bound_agent_id: z.string().nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
created_by: z.string().nullish(),
|
||||
@ -3507,6 +3514,7 @@ export const zAppDetailWithSiteWritable = z.object({
|
||||
mode: z.string(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
name: z.string(),
|
||||
role: z.string().nullish(),
|
||||
site: zSiteWritable.nullish(),
|
||||
tags: z.array(zTag).optional(),
|
||||
tracing: zJsonValue.nullish(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user