feat: add DTO for agent api (#36797)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
zyssyz123 2026-05-29 11:36:41 +08:00 committed by GitHub
parent a180ab19e4
commit 2cc567c6a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1696 additions and 467 deletions

View File

@ -1,9 +1,17 @@
from flask_restx import Resource
from controllers.common.schema import register_schema_models
from controllers.common.schema import register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.app.wraps import get_app_model
from controllers.console.wraps import account_initialization_required, edit_permission_required, setup_required
from fields.agent_fields import (
AgentAppComposerResponse,
AgentComposerCandidatesResponse,
AgentComposerImpactResponse,
AgentComposerValidateResponse,
WorkflowAgentComposerResponse,
)
from libs.helper import dump_response
from libs.login import current_account_with_tenant, login_required
from models.model import App, AppMode
from services.agent.composer_service import AgentComposerService
@ -11,23 +19,40 @@ from services.agent.composer_validator import ComposerConfigValidator
from services.entities.agent_entities import ComposerSavePayload
register_schema_models(console_ns, ComposerSavePayload)
register_response_schema_models(
console_ns,
AgentAppComposerResponse,
AgentComposerCandidatesResponse,
AgentComposerImpactResponse,
AgentComposerValidateResponse,
WorkflowAgentComposerResponse,
)
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/agent-composer")
class WorkflowAgentComposerApi(Resource):
@console_ns.response(
200, "Workflow agent composer state", console_ns.models[WorkflowAgentComposerResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@get_app_model(mode=[AppMode.WORKFLOW, AppMode.ADVANCED_CHAT])
def get(self, app_model: App, node_id: str):
_, tenant_id = current_account_with_tenant()
return AgentComposerService.load_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
return dump_response(
WorkflowAgentComposerResponse,
AgentComposerService.load_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
),
)
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(
200, "Workflow agent composer saved", console_ns.models[WorkflowAgentComposerResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@ -36,18 +61,24 @@ class WorkflowAgentComposerApi(Resource):
def put(self, app_model: App, node_id: str):
account, tenant_id = current_account_with_tenant()
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
return AgentComposerService.save_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
account_id=account.id,
payload=payload,
return dump_response(
WorkflowAgentComposerResponse,
AgentComposerService.save_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
account_id=account.id,
payload=payload,
),
)
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/agent-composer/validate")
class WorkflowAgentComposerValidateApi(Resource):
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(
200, "Workflow agent composer validation result", console_ns.models[AgentComposerValidateResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@ -55,21 +86,29 @@ class WorkflowAgentComposerValidateApi(Resource):
def post(self, app_model: App, node_id: str):
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
ComposerConfigValidator.validate_save_payload(payload)
return {"result": "success", "errors": []}
return dump_response(AgentComposerValidateResponse, {"result": "success", "errors": []})
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/agent-composer/candidates")
class WorkflowAgentComposerCandidatesApi(Resource):
@console_ns.response(
200, "Workflow agent composer candidates", console_ns.models[AgentComposerCandidatesResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@get_app_model(mode=[AppMode.WORKFLOW, AppMode.ADVANCED_CHAT])
def get(self, app_model: App, node_id: str):
return AgentComposerService.get_workflow_candidates(app_id=app_model.id)
return dump_response(
AgentComposerCandidatesResponse,
AgentComposerService.get_workflow_candidates(app_id=app_model.id),
)
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/agent-composer/impact")
class WorkflowAgentComposerImpactApi(Resource):
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(200, "Workflow agent composer impact", console_ns.models[AgentComposerImpactResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -79,13 +118,21 @@ class WorkflowAgentComposerImpactApi(Resource):
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
current_snapshot_id = payload.binding.current_snapshot_id if payload.binding else None
if not current_snapshot_id:
return {"current_snapshot_id": None, "workflow_node_count": 0, "bindings": []}
return AgentComposerService.calculate_impact(tenant_id=tenant_id, current_snapshot_id=current_snapshot_id)
return dump_response(
AgentComposerImpactResponse, {"current_snapshot_id": None, "workflow_node_count": 0, "bindings": []}
)
return dump_response(
AgentComposerImpactResponse,
AgentComposerService.calculate_impact(tenant_id=tenant_id, current_snapshot_id=current_snapshot_id),
)
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/agent-composer/save-to-roster")
class WorkflowAgentComposerSaveToRosterApi(Resource):
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(
200, "Workflow agent composer saved to roster", console_ns.models[WorkflowAgentComposerResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@ -94,26 +141,34 @@ class WorkflowAgentComposerSaveToRosterApi(Resource):
def post(self, app_model: App, node_id: str):
account, tenant_id = current_account_with_tenant()
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
return AgentComposerService.save_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
account_id=account.id,
payload=payload,
return dump_response(
WorkflowAgentComposerResponse,
AgentComposerService.save_workflow_composer(
tenant_id=tenant_id,
app_id=app_model.id,
node_id=node_id,
account_id=account.id,
payload=payload,
),
)
@console_ns.route("/apps/<uuid:app_id>/agent-composer")
class AgentAppComposerApi(Resource):
@console_ns.response(200, "Agent app composer state", console_ns.models[AgentAppComposerResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@get_app_model()
def get(self, app_model: App):
_, tenant_id = current_account_with_tenant()
return AgentComposerService.load_agent_app_composer(tenant_id=tenant_id, app_id=app_model.id)
return dump_response(
AgentAppComposerResponse,
AgentComposerService.load_agent_app_composer(tenant_id=tenant_id, app_id=app_model.id),
)
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(200, "Agent app composer saved", console_ns.models[AgentAppComposerResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -122,17 +177,23 @@ class AgentAppComposerApi(Resource):
def put(self, app_model: App):
account, tenant_id = current_account_with_tenant()
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
return AgentComposerService.save_agent_app_composer(
tenant_id=tenant_id,
app_id=app_model.id,
account_id=account.id,
payload=payload,
return dump_response(
AgentAppComposerResponse,
AgentComposerService.save_agent_app_composer(
tenant_id=tenant_id,
app_id=app_model.id,
account_id=account.id,
payload=payload,
),
)
@console_ns.route("/apps/<uuid:app_id>/agent-composer/validate")
class AgentAppComposerValidateApi(Resource):
@console_ns.expect(console_ns.models[ComposerSavePayload.__name__])
@console_ns.response(
200, "Agent app composer validation result", console_ns.models[AgentComposerValidateResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@ -140,14 +201,20 @@ class AgentAppComposerValidateApi(Resource):
def post(self, app_model: App):
payload = ComposerSavePayload.model_validate(console_ns.payload or {})
ComposerConfigValidator.validate_save_payload(payload)
return {"result": "success", "errors": []}
return dump_response(AgentComposerValidateResponse, {"result": "success", "errors": []})
@console_ns.route("/apps/<uuid:app_id>/agent-composer/candidates")
class AgentAppComposerCandidatesApi(Resource):
@console_ns.response(
200, "Agent app composer candidates", console_ns.models[AgentComposerCandidatesResponse.__name__]
)
@setup_required
@login_required
@account_initialization_required
@get_app_model()
def get(self, app_model: App):
return AgentComposerService.get_agent_app_candidates(app_id=app_model.id)
return dump_response(
AgentComposerCandidatesResponse,
AgentComposerService.get_agent_app_candidates(app_id=app_model.id),
)

View File

@ -4,10 +4,18 @@ from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field
from controllers.common.schema import register_schema_models
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.wraps import account_initialization_required, edit_permission_required, setup_required
from extensions.ext_database import db
from fields.agent_fields import (
AgentConfigSnapshotDetailResponse,
AgentConfigSnapshotListResponse,
AgentInviteOptionsResponse,
AgentRosterListResponse,
AgentRosterResponse,
)
from libs.helper import dump_response
from libs.login import current_account_with_tenant, login_required
from services.agent.roster_service import AgentRosterService
from services.entities.agent_entities import RosterAgentCreatePayload, RosterAgentUpdatePayload, RosterListQuery
@ -29,6 +37,14 @@ register_schema_models(
RosterAgentUpdatePayload,
RosterListQuery,
)
register_response_schema_models(
console_ns,
AgentConfigSnapshotDetailResponse,
AgentConfigSnapshotListResponse,
AgentInviteOptionsResponse,
AgentRosterListResponse,
AgentRosterResponse,
)
def _agent_roster_service() -> AgentRosterService:
@ -37,17 +53,23 @@ def _agent_roster_service() -> AgentRosterService:
@console_ns.route("/agents")
class AgentRosterListApi(Resource):
@console_ns.doc(params=query_params_from_model(RosterListQuery))
@console_ns.response(200, "Agent roster list", console_ns.models[AgentRosterListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
def get(self):
_, tenant_id = current_account_with_tenant()
query = RosterListQuery.model_validate(request.args.to_dict(flat=True))
return _agent_roster_service().list_roster_agents(
tenant_id=tenant_id, page=query.page, limit=query.limit, keyword=query.keyword
return dump_response(
AgentRosterListResponse,
_agent_roster_service().list_roster_agents(
tenant_id=tenant_id, page=query.page, limit=query.limit, keyword=query.keyword
),
)
@console_ns.expect(console_ns.models[RosterAgentCreatePayload.__name__])
@console_ns.response(201, "Agent created", console_ns.models[AgentRosterResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -57,36 +79,49 @@ class AgentRosterListApi(Resource):
payload = RosterAgentCreatePayload.model_validate(console_ns.payload or {})
service = _agent_roster_service()
agent = service.create_roster_agent(tenant_id=tenant_id, account_id=account.id, payload=payload)
return service.get_roster_agent_detail(tenant_id=tenant_id, agent_id=agent.id), 201
return dump_response(
AgentRosterResponse,
service.get_roster_agent_detail(tenant_id=tenant_id, agent_id=agent.id),
), 201
@console_ns.route("/agents/invite-options")
class AgentInviteOptionsApi(Resource):
@console_ns.doc(params=query_params_from_model(AgentInviteOptionsQuery))
@console_ns.response(200, "Agent invite options", console_ns.models[AgentInviteOptionsResponse.__name__])
@setup_required
@login_required
@account_initialization_required
def get(self):
_, tenant_id = current_account_with_tenant()
query = AgentInviteOptionsQuery.model_validate(request.args.to_dict(flat=True))
return _agent_roster_service().list_invite_options(
tenant_id=tenant_id,
page=query.page,
limit=query.limit,
keyword=query.keyword,
app_id=query.app_id,
return dump_response(
AgentInviteOptionsResponse,
_agent_roster_service().list_invite_options(
tenant_id=tenant_id,
page=query.page,
limit=query.limit,
keyword=query.keyword,
app_id=query.app_id,
),
)
@console_ns.route("/agents/<uuid:agent_id>")
class AgentRosterDetailApi(Resource):
@console_ns.response(200, "Agent detail", console_ns.models[AgentRosterResponse.__name__])
@setup_required
@login_required
@account_initialization_required
def get(self, agent_id: UUID):
_, tenant_id = current_account_with_tenant()
return _agent_roster_service().get_roster_agent_detail(tenant_id=tenant_id, agent_id=str(agent_id))
return dump_response(
AgentRosterResponse,
_agent_roster_service().get_roster_agent_detail(tenant_id=tenant_id, agent_id=str(agent_id)),
)
@console_ns.expect(console_ns.models[RosterAgentUpdatePayload.__name__])
@console_ns.response(200, "Agent updated", console_ns.models[AgentRosterResponse.__name__])
@setup_required
@login_required
@account_initialization_required
@ -94,10 +129,14 @@ class AgentRosterDetailApi(Resource):
def patch(self, agent_id: UUID):
account, tenant_id = current_account_with_tenant()
payload = RosterAgentUpdatePayload.model_validate(console_ns.payload or {})
return _agent_roster_service().update_roster_agent(
tenant_id=tenant_id, agent_id=str(agent_id), account_id=account.id, payload=payload
return dump_response(
AgentRosterResponse,
_agent_roster_service().update_roster_agent(
tenant_id=tenant_id, agent_id=str(agent_id), account_id=account.id, payload=payload
),
)
@console_ns.response(204, "Agent archived")
@setup_required
@login_required
@account_initialization_required
@ -110,23 +149,31 @@ class AgentRosterDetailApi(Resource):
@console_ns.route("/agents/<uuid:agent_id>/versions")
class AgentRosterVersionsApi(Resource):
@console_ns.response(200, "Agent versions", console_ns.models[AgentConfigSnapshotListResponse.__name__])
@setup_required
@login_required
@account_initialization_required
def get(self, agent_id: UUID):
_, tenant_id = current_account_with_tenant()
return {"data": _agent_roster_service().list_agent_versions(tenant_id=tenant_id, agent_id=str(agent_id))}
return dump_response(
AgentConfigSnapshotListResponse,
{"data": _agent_roster_service().list_agent_versions(tenant_id=tenant_id, agent_id=str(agent_id))},
)
@console_ns.route("/agents/<uuid:agent_id>/versions/<uuid:version_id>")
class AgentRosterVersionDetailApi(Resource):
@console_ns.response(200, "Agent version detail", console_ns.models[AgentConfigSnapshotDetailResponse.__name__])
@setup_required
@login_required
@account_initialization_required
def get(self, agent_id: UUID, version_id: UUID):
_, tenant_id = current_account_with_tenant()
return _agent_roster_service().get_agent_version_detail(
tenant_id=tenant_id,
agent_id=str(agent_id),
version_id=str(version_id),
return dump_response(
AgentConfigSnapshotDetailResponse,
_agent_roster_service().get_agent_version_detail(
tenant_id=tenant_id,
agent_id=str(agent_id),
version_id=str(version_id),
),
)

192
api/fields/agent_fields.py Normal file
View File

@ -0,0 +1,192 @@
from typing import Any, Literal
from pydantic import Field
from fields.base import ResponseModel
from models.agent import (
AgentConfigRevisionOperation,
AgentIconType,
AgentKind,
AgentScope,
AgentSource,
AgentStatus,
WorkflowAgentBindingType,
)
from models.agent_config_entities import (
AgentSoulConfig,
DeclaredOutputConfig,
DeclaredOutputType,
WorkflowNodeJobConfig,
)
from services.entities.agent_entities import (
ComposerCandidateCapabilities,
ComposerSaveStrategy,
ComposerVariant,
)
class AgentConfigSnapshotSummaryResponse(ResponseModel):
id: str
agent_id: str | None = None
version: int
summary: str | None = None
version_note: str | None = None
created_by: str | None = None
created_at: str | None = None
class AgentRosterResponse(ResponseModel):
id: str
name: str
description: str
icon_type: AgentIconType | None = None
icon: str | None = None
icon_background: str | None = None
agent_kind: AgentKind
scope: AgentScope
source: AgentSource
app_id: str | None = None
workflow_id: str | None = None
workflow_node_id: str | None = None
active_config_snapshot_id: str | None = None
active_config_snapshot: AgentConfigSnapshotSummaryResponse | None = None
status: AgentStatus
created_by: str | None = None
updated_by: str | None = None
archived_by: str | None = None
archived_at: str | None = None
created_at: str | None = None
updated_at: str | None = None
class AgentInviteOptionResponse(AgentRosterResponse):
is_in_current_workflow: bool = False
in_current_workflow_count: int = 0
existing_node_ids: list[str] = Field(default_factory=list)
class AgentRosterListResponse(ResponseModel):
data: list[AgentRosterResponse]
page: int
limit: int
total: int
has_more: bool
class AgentInviteOptionsResponse(ResponseModel):
data: list[AgentInviteOptionResponse]
page: int
limit: int
total: int
has_more: bool
class AgentConfigRevisionResponse(ResponseModel):
id: str
previous_snapshot_id: str | None = None
current_snapshot_id: str
revision: int
operation: AgentConfigRevisionOperation
summary: str | None = None
version_note: str | None = None
created_by: str | None = None
created_at: str | None = None
class AgentConfigSnapshotDetailResponse(AgentConfigSnapshotSummaryResponse):
config_snapshot: AgentSoulConfig
revisions: list[AgentConfigRevisionResponse] = Field(default_factory=list)
class AgentConfigSnapshotListResponse(ResponseModel):
data: list[AgentConfigSnapshotSummaryResponse]
class AgentComposerAgentResponse(ResponseModel):
id: str
name: str
description: str
scope: AgentScope
status: AgentStatus
active_config_snapshot_id: str | None = None
class AgentComposerBindingResponse(ResponseModel):
id: str
binding_type: WorkflowAgentBindingType
agent_id: str | None = None
current_snapshot_id: str | None = None
workflow_id: str
node_id: str
class AgentComposerSoulLockResponse(ResponseModel):
locked: bool
can_unlock: bool = False
reason: str | None = None
class AgentComposerImpactBindingResponse(ResponseModel):
app_id: str
workflow_id: str
node_id: str
class AgentComposerImpactResponse(ResponseModel):
current_snapshot_id: str | None = None
workflow_node_count: int
bindings: list[AgentComposerImpactBindingResponse] = Field(default_factory=list)
class WorkflowAgentComposerResponse(ResponseModel):
variant: Literal[ComposerVariant.WORKFLOW]
agent: AgentComposerAgentResponse | None = None
active_config_snapshot: AgentConfigSnapshotSummaryResponse | None = None
binding: AgentComposerBindingResponse | None = None
soul_lock: AgentComposerSoulLockResponse
agent_soul: AgentSoulConfig
node_job: WorkflowNodeJobConfig
effective_declared_outputs: list[DeclaredOutputConfig] = Field(default_factory=list)
save_options: list[ComposerSaveStrategy]
impact_summary: AgentComposerImpactResponse | None = None
app_id: str | None = None
workflow_id: str | None = None
node_id: str | None = None
class AgentAppComposerResponse(ResponseModel):
variant: Literal[ComposerVariant.AGENT_APP]
agent: AgentComposerAgentResponse
active_config_snapshot: AgentConfigSnapshotSummaryResponse
agent_soul: AgentSoulConfig
save_options: list[ComposerSaveStrategy]
class AgentComposerValidateResponse(ResponseModel):
result: Literal["success"]
errors: list[str] = Field(default_factory=list)
class AgentComposerNodeJobCandidatesResponse(ResponseModel):
previous_node_outputs: list[dict[str, Any]] = Field(default_factory=list)
declare_output_types: list[DeclaredOutputType] = Field(default_factory=list)
human_contacts: list[dict[str, Any]] = Field(default_factory=list)
class AgentComposerSoulCandidatesResponse(ResponseModel):
skills_files: list[dict[str, Any]] = Field(default_factory=list)
dify_tools: list[dict[str, Any]] = Field(default_factory=list)
cli_tools: list[dict[str, Any]] = Field(default_factory=list)
knowledge_datasets: list[dict[str, Any]] = Field(default_factory=list)
human_contacts: list[dict[str, Any]] = Field(default_factory=list)
class AgentComposerCandidatesResponse(ResponseModel):
variant: ComposerVariant
allowed_node_job_candidates: AgentComposerNodeJobCandidatesResponse = Field(
default_factory=AgentComposerNodeJobCandidatesResponse
)
allowed_soul_candidates: AgentComposerSoulCandidatesResponse = Field(
default_factory=AgentComposerSoulCandidatesResponse
)
capabilities: ComposerCandidateCapabilities = Field(default_factory=ComposerCandidateCapabilities)

View File

@ -343,11 +343,19 @@ Check if activation token is valid
### /agents
#### GET
##### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| keyword | query | | No | string |
| limit | query | | No | integer |
| page | query | | No | integer |
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent roster list | [AgentRosterListResponse](#agentrosterlistresponse) |
#### POST
##### Parameters
@ -358,18 +366,27 @@ Check if activation token is valid
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 201 | Agent created | [AgentRosterResponse](#agentrosterresponse) |
### /agents/invite-options
#### GET
##### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | query | Workflow app id for in-current-workflow markers | No | string |
| keyword | query | | No | string |
| limit | query | | No | integer |
| page | query | | No | integer |
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent invite options | [AgentInviteOptionsResponse](#agentinviteoptionsresponse) |
### /agents/{agent_id}
@ -384,7 +401,7 @@ Check if activation token is valid
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| 204 | Agent archived |
#### GET
##### Parameters
@ -395,9 +412,9 @@ Check if activation token is valid
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent detail | [AgentRosterResponse](#agentrosterresponse) |
#### PATCH
##### Parameters
@ -409,9 +426,9 @@ Check if activation token is valid
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent updated | [AgentRosterResponse](#agentrosterresponse) |
### /agents/{agent_id}/versions
@ -424,9 +441,9 @@ Check if activation token is valid
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent versions | [AgentConfigSnapshotListResponse](#agentconfigsnapshotlistresponse) |
### /agents/{agent_id}/versions/{version_id}
@ -440,9 +457,9 @@ Check if activation token is valid
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent version detail | [AgentConfigSnapshotDetailResponse](#agentconfigsnapshotdetailresponse) |
### /all-workspaces
@ -978,9 +995,9 @@ Run draft workflow for advanced chat application
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent app composer state | [AgentAppComposerResponse](#agentappcomposerresponse) |
#### PUT
##### Parameters
@ -992,9 +1009,9 @@ Run draft workflow for advanced chat application
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent app composer saved | [AgentAppComposerResponse](#agentappcomposerresponse) |
### /apps/{app_id}/agent-composer/candidates
@ -1007,9 +1024,9 @@ Run draft workflow for advanced chat application
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent app composer candidates | [AgentComposerCandidatesResponse](#agentcomposercandidatesresponse) |
### /apps/{app_id}/agent-composer/validate
@ -1023,9 +1040,9 @@ Run draft workflow for advanced chat application
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent app composer validation result | [AgentComposerValidateResponse](#agentcomposervalidateresponse) |
### /apps/{app_id}/agent/logs
@ -3224,9 +3241,9 @@ Run draft workflow loop node
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer state | [WorkflowAgentComposerResponse](#workflowagentcomposerresponse) |
#### PUT
##### Parameters
@ -3239,9 +3256,9 @@ Run draft workflow loop node
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer saved | [WorkflowAgentComposerResponse](#workflowagentcomposerresponse) |
### /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/candidates
@ -3255,9 +3272,9 @@ Run draft workflow loop node
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer candidates | [AgentComposerCandidatesResponse](#agentcomposercandidatesresponse) |
### /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/impact
@ -3268,12 +3285,13 @@ Run draft workflow loop node
| ---- | ---------- | ----------- | -------- | ------ |
| app_id | path | | Yes | string |
| node_id | path | | Yes | string |
| payload | body | | Yes | [ComposerSavePayload](#composersavepayload) |
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer impact | [AgentComposerImpactResponse](#agentcomposerimpactresponse) |
### /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/save-to-roster
@ -3288,9 +3306,9 @@ Run draft workflow loop node
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer saved to roster | [WorkflowAgentComposerResponse](#workflowagentcomposerresponse) |
### /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/validate
@ -3305,9 +3323,9 @@ Run draft workflow loop node
##### Responses
| Code | Description |
| ---- | ----------- |
| 200 | Success |
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Workflow agent composer validation result | [AgentComposerValidateResponse](#agentcomposervalidateresponse) |
### /apps/{app_id}/workflows/draft/nodes/{node_id}/last-run
@ -10651,6 +10669,150 @@ Get banner list
| model_mode | string | Model mode | Yes |
| model_name | string | Model name | Yes |
#### AgentAppComposerResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| active_config_snapshot | [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) | | Yes |
| agent | [AgentComposerAgentResponse](#agentcomposeragentresponse) | | Yes |
| agent_soul | [AgentSoulConfig](#agentsoulconfig) | | Yes |
| save_options | [ [ComposerSaveStrategy](#composersavestrategy) ] | | Yes |
| variant | string | | Yes |
#### AgentComposerAgentResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| active_config_snapshot_id | string | | No |
| description | string | | Yes |
| id | string | | Yes |
| name | string | | Yes |
| scope | [AgentScope](#agentscope) | | Yes |
| status | [AgentStatus](#agentstatus) | | Yes |
#### AgentComposerBindingResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| agent_id | string | | No |
| binding_type | [WorkflowAgentBindingType](#workflowagentbindingtype) | | Yes |
| current_snapshot_id | string | | No |
| id | string | | Yes |
| node_id | string | | Yes |
| workflow_id | string | | Yes |
#### AgentComposerCandidatesResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| allowed_node_job_candidates | [AgentComposerNodeJobCandidatesResponse](#agentcomposernodejobcandidatesresponse) | | No |
| allowed_soul_candidates | [AgentComposerSoulCandidatesResponse](#agentcomposersoulcandidatesresponse) | | No |
| capabilities | [ComposerCandidateCapabilities](#composercandidatecapabilities) | | No |
| variant | [ComposerVariant](#composervariant) | | Yes |
#### AgentComposerImpactBindingResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| app_id | string | | Yes |
| node_id | string | | Yes |
| workflow_id | string | | Yes |
#### AgentComposerImpactResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| bindings | [ [AgentComposerImpactBindingResponse](#agentcomposerimpactbindingresponse) ] | | No |
| current_snapshot_id | string | | No |
| workflow_node_count | integer | | Yes |
#### AgentComposerNodeJobCandidatesResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| declare_output_types | [ [DeclaredOutputType](#declaredoutputtype) ] | | No |
| human_contacts | [ object ] | | No |
| previous_node_outputs | [ object ] | | No |
#### AgentComposerSoulCandidatesResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| cli_tools | [ object ] | | No |
| dify_tools | [ object ] | | No |
| human_contacts | [ object ] | | No |
| knowledge_datasets | [ object ] | | No |
| skills_files | [ object ] | | No |
#### AgentComposerSoulLockResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| can_unlock | boolean | | No |
| locked | boolean | | Yes |
| reason | string | | No |
#### AgentComposerValidateResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| errors | [ string ] | | No |
| result | string | | Yes |
#### AgentConfigRevisionOperation
Audit operation recorded for Agent Soul version/revision changes.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AgentConfigRevisionOperation | string | Audit operation recorded for Agent Soul version/revision changes. | |
#### AgentConfigRevisionResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | string | | No |
| created_by | string | | No |
| current_snapshot_id | string | | Yes |
| id | string | | Yes |
| operation | [AgentConfigRevisionOperation](#agentconfigrevisionoperation) | | Yes |
| previous_snapshot_id | string | | No |
| revision | integer | | Yes |
| summary | string | | No |
| version_note | string | | No |
#### AgentConfigSnapshotDetailResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| agent_id | string | | No |
| config_snapshot | [AgentSoulConfig](#agentsoulconfig) | | Yes |
| created_at | string | | No |
| created_by | string | | No |
| id | string | | Yes |
| revisions | [ [AgentConfigRevisionResponse](#agentconfigrevisionresponse) ] | | No |
| summary | string | | No |
| version | integer | | Yes |
| version_note | string | | No |
#### AgentConfigSnapshotListResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| data | [ [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) ] | | Yes |
#### AgentConfigSnapshotSummaryResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| agent_id | string | | No |
| created_at | string | | No |
| created_by | string | | No |
| id | string | | Yes |
| summary | string | | No |
| version | integer | | Yes |
| version_note | string | | No |
#### AgentIconType
Supported icon storage formats for Agent roster entries.
@ -10665,6 +10827,35 @@ Supported icon storage formats for Agent roster entries.
| ---- | ---- | ----------- | -------- |
| agent_id | string | | Yes |
#### AgentInviteOptionResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| active_config_snapshot | [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) | | No |
| active_config_snapshot_id | string | | No |
| agent_kind | [AgentKind](#agentkind) | | Yes |
| app_id | string | | No |
| archived_at | string | | No |
| archived_by | string | | No |
| created_at | string | | No |
| created_by | string | | No |
| description | string | | Yes |
| existing_node_ids | [ string ] | | No |
| icon | string | | No |
| icon_background | string | | No |
| icon_type | [AgentIconType](#agenticontype) | | No |
| id | string | | Yes |
| in_current_workflow_count | integer | | No |
| is_in_current_workflow | boolean | | No |
| name | string | | Yes |
| scope | [AgentScope](#agentscope) | | Yes |
| source | [AgentSource](#agentsource) | | Yes |
| status | [AgentStatus](#agentstatus) | | Yes |
| updated_at | string | | No |
| updated_by | string | | No |
| workflow_id | string | | No |
| workflow_node_id | string | | No |
#### AgentInviteOptionsQuery
| Name | Type | Description | Required |
@ -10674,6 +10865,27 @@ Supported icon storage formats for Agent roster entries.
| limit | integer | | No |
| page | integer | | No |
#### AgentInviteOptionsResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| data | [ [AgentInviteOptionResponse](#agentinviteoptionresponse) ] | | Yes |
| has_more | boolean | | Yes |
| limit | integer | | Yes |
| page | integer | | Yes |
| total | integer | | Yes |
#### AgentKind
Agent implementation family.
This leaves room for future non-Dify agent implementations while keeping
the current roster/workflow APIs scoped to Dify Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AgentKind | string | Agent implementation family. This leaves room for future non-Dify agent implementations while keeping the current roster/workflow APIs scoped to Dify Agent. | |
#### AgentKnowledgeQueryMode
| Name | Type | Description | Required |
@ -10687,6 +10899,50 @@ Supported icon storage formats for Agent roster entries.
| conversation_id | string | Conversation UUID | Yes |
| message_id | string | Message UUID | Yes |
#### AgentRosterListResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| data | [ [AgentRosterResponse](#agentrosterresponse) ] | | Yes |
| has_more | boolean | | Yes |
| limit | integer | | Yes |
| page | integer | | Yes |
| total | integer | | Yes |
#### AgentRosterResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| active_config_snapshot | [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) | | No |
| active_config_snapshot_id | string | | No |
| agent_kind | [AgentKind](#agentkind) | | Yes |
| app_id | string | | No |
| archived_at | string | | No |
| archived_by | string | | No |
| created_at | string | | No |
| created_by | string | | No |
| description | string | | Yes |
| icon | string | | No |
| icon_background | string | | No |
| icon_type | [AgentIconType](#agenticontype) | | No |
| id | string | | Yes |
| name | string | | Yes |
| scope | [AgentScope](#agentscope) | | Yes |
| source | [AgentSource](#agentsource) | | Yes |
| status | [AgentStatus](#agentstatus) | | Yes |
| updated_at | string | | No |
| updated_by | string | | No |
| workflow_id | string | | No |
| workflow_node_id | string | | No |
#### AgentScope
Visibility and lifecycle scope of an Agent record.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AgentScope | string | Visibility and lifecycle scope of an Agent record. | |
#### AgentSoulConfig
| Name | Type | Description | Required |
@ -10821,6 +11077,22 @@ Reference to model credentials resolved only at runtime.
| cli_tools | [ object ] | | No |
| dify_tools | [ [AgentSoulDifyToolConfig](#agentsouldifytoolconfig) ] | | No |
#### AgentSource
Origin that created or imported the Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AgentSource | string | Origin that created or imported the Agent. | |
#### AgentStatus
Soft lifecycle state for Agent records.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| AgentStatus | string | Soft lifecycle state for Agent records. | |
#### AgentThought
| Name | Type | Description | Required |
@ -11528,6 +11800,12 @@ Button styles for user actions.
| binding_type | string | *Enum:* `"inline_agent"`, `"roster_agent"` | Yes |
| current_snapshot_id | string | | No |
#### ComposerCandidateCapabilities
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| human_roster_available | boolean | | No |
#### ComposerSavePayload
| Name | Type | Description | Required |
@ -15369,6 +15647,32 @@ in form definiton, or a variable while the workflow is running.
| embedding_provider_name | string | | Yes |
| vector_weight | number | | Yes |
#### WorkflowAgentBindingType
How a workflow node is bound to an Agent.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| WorkflowAgentBindingType | string | How a workflow node is bound to an Agent. | |
#### WorkflowAgentComposerResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| active_config_snapshot | [AgentConfigSnapshotSummaryResponse](#agentconfigsnapshotsummaryresponse) | | No |
| agent | [AgentComposerAgentResponse](#agentcomposeragentresponse) | | No |
| agent_soul | [AgentSoulConfig](#agentsoulconfig) | | Yes |
| app_id | string | | No |
| binding | [AgentComposerBindingResponse](#agentcomposerbindingresponse) | | No |
| effective_declared_outputs | [ [DeclaredOutputConfig](#declaredoutputconfig) ] | | No |
| impact_summary | [AgentComposerImpactResponse](#agentcomposerimpactresponse) | | No |
| node_id | string | | No |
| node_job | [WorkflowNodeJobConfig](#workflownodejobconfig) | | Yes |
| save_options | [ [ComposerSaveStrategy](#composersavestrategy) ] | | Yes |
| soul_lock | [AgentComposerSoulLockResponse](#agentcomposersoullockresponse) | | Yes |
| variant | string | | Yes |
| workflow_id | string | | No |
#### WorkflowAppLogPaginationResponse
| Name | Type | Description | Required |

View File

@ -30,6 +30,90 @@ def _unwrap(method):
return method
def _agent_response(agent_id: str = "agent-1") -> dict:
return {
"id": agent_id,
"name": "Analyst",
"description": "",
"icon_type": None,
"icon": None,
"icon_background": None,
"agent_kind": "dify_agent",
"scope": "roster",
"source": "agent_app",
"app_id": None,
"workflow_id": None,
"workflow_node_id": None,
"active_config_snapshot_id": "version-1",
"active_config_snapshot": _version_response(),
"status": "active",
"created_by": "account-1",
"updated_by": "account-1",
"archived_by": None,
"archived_at": None,
"created_at": None,
"updated_at": None,
}
def _version_response(version_id: str = "version-1") -> dict:
return {
"id": version_id,
"agent_id": "agent-1",
"version": 1,
"summary": None,
"version_note": None,
"created_by": "account-1",
"created_at": None,
}
def _workflow_composer_response(**overrides) -> dict:
response = {
"variant": "workflow",
"agent": None,
"active_config_snapshot": None,
"binding": None,
"soul_lock": {"locked": False, "can_unlock": False, "reason": "workflow_only_empty"},
"agent_soul": {},
"node_job": {},
"effective_declared_outputs": [],
"save_options": ["node_job_only"],
"impact_summary": None,
"app_id": "app-1",
"workflow_id": "workflow-1",
"node_id": "node-1",
}
response.update(overrides)
return response
def _agent_app_composer_response() -> dict:
return {
"variant": "agent_app",
"agent": {
"id": "agent-1",
"name": "Analyst",
"description": "",
"scope": "roster",
"status": "active",
"active_config_snapshot_id": "version-1",
},
"active_config_snapshot": _version_response(),
"agent_soul": {},
"save_options": ["save_to_current_version", "save_as_new_version"],
}
def _candidates_response(variant: str) -> dict:
return {
"variant": variant,
"allowed_node_job_candidates": {},
"allowed_soul_candidates": {},
"capabilities": {"human_roster_available": False},
}
@pytest.fixture
def account():
return SimpleNamespace(id="account-1")
@ -67,14 +151,15 @@ def test_roster_list_post_creates_agent_and_returns_detail(app, monkeypatch):
monkeypatch.setattr(
roster_controller.AgentRosterService,
"get_roster_agent_detail",
lambda _self, **kwargs: {"id": kwargs["agent_id"], "tenant_id": kwargs["tenant_id"]},
lambda _self, **kwargs: _agent_response(kwargs["agent_id"]),
)
with app.test_request_context(json={"name": "Analyst", "agent_soul": {"prompt": {"system_prompt": "x"}}}):
result, status = _unwrap(AgentRosterListApi.post)(AgentRosterListApi())
assert status == 201
assert result == {"id": "agent-1", "tenant_id": "tenant-1"}
assert result["id"] == "agent-1"
assert result["agent_kind"] == "dify_agent"
def test_invite_options_get_parses_app_id(app, monkeypatch):
@ -82,14 +167,14 @@ def test_invite_options_get_parses_app_id(app, monkeypatch):
def list_invite_options(_self, **kwargs):
captured.update(kwargs)
return {"data": []}
return {"data": [], "page": kwargs["page"], "limit": kwargs["limit"], "total": 0, "has_more": False}
monkeypatch.setattr(roster_controller.AgentRosterService, "list_invite_options", list_invite_options)
with app.test_request_context("/console/api/agents/invite-options?page=1&limit=10&app_id=app-1"):
result = _unwrap(AgentInviteOptionsApi.get)(AgentInviteOptionsApi())
assert result == {"data": []}
assert result == {"data": [], "page": 1, "limit": 10, "total": 0, "has_more": False}
assert captured == {"tenant_id": "tenant-1", "page": 1, "limit": 10, "keyword": None, "app_id": "app-1"}
@ -100,12 +185,12 @@ def test_roster_detail_patch_delete_and_versions_call_services(app, monkeypatch)
monkeypatch.setattr(
roster_controller.AgentRosterService,
"get_roster_agent_detail",
lambda _self, **kwargs: {"id": kwargs["agent_id"]},
lambda _self, **kwargs: _agent_response(kwargs["agent_id"]),
)
monkeypatch.setattr(
roster_controller.AgentRosterService,
"update_roster_agent",
lambda _self, **kwargs: {"id": kwargs["agent_id"], "description": kwargs["payload"].description},
lambda _self, **kwargs: {**_agent_response(kwargs["agent_id"]), "description": kwargs["payload"].description},
)
monkeypatch.setattr(
roster_controller.AgentRosterService,
@ -115,12 +200,29 @@ def test_roster_detail_patch_delete_and_versions_call_services(app, monkeypatch)
monkeypatch.setattr(
roster_controller.AgentRosterService,
"list_agent_versions",
lambda _self, **kwargs: [{"id": "version-1"}],
lambda _self, **kwargs: [_version_response()],
)
monkeypatch.setattr(
roster_controller.AgentRosterService,
"get_agent_version_detail",
lambda _self, **kwargs: {"id": kwargs["version_id"], "agent_id": kwargs["agent_id"]},
lambda _self, **kwargs: {
**_version_response(kwargs["version_id"]),
"agent_id": kwargs["agent_id"],
"config_snapshot": {},
"revisions": [
{
"id": "revision-1",
"previous_snapshot_id": None,
"current_snapshot_id": kwargs["version_id"],
"revision": 1,
"operation": "create_version",
"summary": None,
"version_note": None,
"created_by": "account-1",
"created_at": None,
}
],
},
)
assert _unwrap(AgentRosterDetailApi.get)(AgentRosterDetailApi(), agent_id)["id"] == agent_id
@ -128,11 +230,10 @@ def test_roster_detail_patch_delete_and_versions_call_services(app, monkeypatch)
assert _unwrap(AgentRosterDetailApi.patch)(AgentRosterDetailApi(), agent_id)["description"] == "updated"
assert _unwrap(AgentRosterDetailApi.delete)(AgentRosterDetailApi(), agent_id) == ("", 204)
assert archived["account_id"] == "account-1"
assert _unwrap(AgentRosterVersionsApi.get)(AgentRosterVersionsApi(), agent_id) == {"data": [{"id": "version-1"}]}
assert _unwrap(AgentRosterVersionDetailApi.get)(AgentRosterVersionDetailApi(), agent_id, version_id) == {
"id": version_id,
"agent_id": agent_id,
}
assert _unwrap(AgentRosterVersionsApi.get)(AgentRosterVersionsApi(), agent_id)["data"][0]["id"] == "version-1"
version_detail = _unwrap(AgentRosterVersionDetailApi.get)(AgentRosterVersionDetailApi(), agent_id, version_id)
assert version_detail["id"] == version_id
assert version_detail["agent_id"] == agent_id
def test_workflow_composer_get_put_validate_candidates_impact_and_save(app, monkeypatch):
@ -145,50 +246,52 @@ def test_workflow_composer_get_put_validate_candidates_impact_and_save(app, monk
monkeypatch.setattr(
composer_controller.AgentComposerService,
"load_workflow_composer",
lambda **kwargs: {"node_id": kwargs["node_id"]},
lambda **kwargs: _workflow_composer_response(node_id=kwargs["node_id"]),
)
monkeypatch.setattr(
composer_controller.AgentComposerService,
"save_workflow_composer",
lambda **kwargs: {"saved": kwargs["payload"].save_strategy.value, "account_id": kwargs["account_id"]},
lambda **kwargs: _workflow_composer_response(save_options=[kwargs["payload"].save_strategy.value]),
)
monkeypatch.setattr(composer_controller.ComposerConfigValidator, "validate_save_payload", lambda payload: None)
monkeypatch.setattr(
composer_controller.AgentComposerService,
"get_workflow_candidates",
lambda **kwargs: {"data": []},
lambda **kwargs: _candidates_response("workflow"),
)
monkeypatch.setattr(
composer_controller.AgentComposerService,
"calculate_impact",
lambda **kwargs: {"current_snapshot_id": kwargs["current_snapshot_id"], "workflow_node_count": 1},
lambda **kwargs: {
"current_snapshot_id": kwargs["current_snapshot_id"],
"workflow_node_count": 1,
"bindings": [],
},
)
assert _unwrap(WorkflowAgentComposerApi.get)(WorkflowAgentComposerApi(), app_model, "node-1") == {
"node_id": "node-1"
}
workflow_state = _unwrap(WorkflowAgentComposerApi.get)(WorkflowAgentComposerApi(), app_model, "node-1")
assert workflow_state["node_id"] == "node-1"
with app.test_request_context(json=payload):
assert _unwrap(WorkflowAgentComposerApi.put)(WorkflowAgentComposerApi(), app_model, "node-1") == {
"saved": "node_job_only",
"account_id": "account-1",
}
saved_state = _unwrap(WorkflowAgentComposerApi.put)(WorkflowAgentComposerApi(), app_model, "node-1")
assert saved_state["save_options"] == ["node_job_only"]
assert _unwrap(WorkflowAgentComposerValidateApi.post)(
WorkflowAgentComposerValidateApi(), app_model, "node-1"
) == {"result": "success", "errors": []}
assert _unwrap(WorkflowAgentComposerCandidatesApi.get)(
WorkflowAgentComposerCandidatesApi(), app_model, "node-1"
) == {"data": []}
assert (
_unwrap(WorkflowAgentComposerCandidatesApi.get)(WorkflowAgentComposerCandidatesApi(), app_model, "node-1")[
"variant"
]
== "workflow"
)
with app.test_request_context(json=payload):
assert _unwrap(WorkflowAgentComposerImpactApi.post)(WorkflowAgentComposerImpactApi(), app_model, "node-1") == {
"current_snapshot_id": "version-1",
"workflow_node_count": 1,
"bindings": [],
}
assert (
_unwrap(WorkflowAgentComposerSaveToRosterApi.post)(
WorkflowAgentComposerSaveToRosterApi(), app_model, "node-1"
)["saved"]
== "node_job_only"
)
assert _unwrap(WorkflowAgentComposerSaveToRosterApi.post)(
WorkflowAgentComposerSaveToRosterApi(), app_model, "node-1"
)["save_options"] == ["node_job_only"]
def test_workflow_impact_returns_empty_without_version(app):
@ -212,28 +315,26 @@ def test_agent_app_composer_get_put_validate_and_candidates(app, monkeypatch):
monkeypatch.setattr(
composer_controller.AgentComposerService,
"load_agent_app_composer",
lambda **kwargs: {"loaded": True},
lambda **kwargs: _agent_app_composer_response(),
)
monkeypatch.setattr(
composer_controller.AgentComposerService,
"save_agent_app_composer",
lambda **kwargs: {"saved": kwargs["payload"].variant.value, "account_id": kwargs["account_id"]},
lambda **kwargs: _agent_app_composer_response(),
)
monkeypatch.setattr(composer_controller.ComposerConfigValidator, "validate_save_payload", lambda payload: None)
monkeypatch.setattr(
composer_controller.AgentComposerService,
"get_agent_app_candidates",
lambda **kwargs: {"data": []},
lambda **kwargs: _candidates_response("agent_app"),
)
assert _unwrap(AgentAppComposerApi.get)(AgentAppComposerApi(), app_model) == {"loaded": True}
assert _unwrap(AgentAppComposerApi.get)(AgentAppComposerApi(), app_model)["variant"] == "agent_app"
with app.test_request_context(json=payload):
assert _unwrap(AgentAppComposerApi.put)(AgentAppComposerApi(), app_model) == {
"saved": "agent_app",
"account_id": "account-1",
}
assert _unwrap(AgentAppComposerApi.put)(AgentAppComposerApi(), app_model)["variant"] == "agent_app"
assert _unwrap(AgentAppComposerValidateApi.post)(AgentAppComposerValidateApi(), app_model) == {
"result": "success",
"errors": [],
}
assert _unwrap(AgentAppComposerCandidatesApi.get)(AgentAppComposerCandidatesApi(), app_model) == {"data": []}
agent_app_candidates = _unwrap(AgentAppComposerCandidatesApi.get)(AgentAppComposerCandidatesApi(), app_model)
assert agent_app_candidates["variant"] == "agent_app"

View File

@ -44,7 +44,7 @@ abstract class FileBasedStore implements Store {
flush(): void {
fs.mkdirSync(dirname(this.filePath), { recursive: true, mode: DIR_PERM })
// we don't handle A-B-A scenario,
// we don't handle A-B-A scenario,
// which is not likely to happen in cli
if (!this.dirty) {
return

View File

@ -12,7 +12,9 @@ import {
zGetAgentsByAgentIdVersionsByVersionIdResponse,
zGetAgentsByAgentIdVersionsPath,
zGetAgentsByAgentIdVersionsResponse,
zGetAgentsInviteOptionsQuery,
zGetAgentsInviteOptionsResponse,
zGetAgentsQuery,
zGetAgentsResponse,
zPatchAgentsByAgentIdBody,
zPatchAgentsByAgentIdPath,
@ -21,22 +23,15 @@ import {
zPostAgentsResponse,
} from './zod.gen'
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const get = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'GET',
operationId: 'getAgentsInviteOptions',
path: '/agents/invite-options',
tags: ['console'],
})
.input(z.object({ query: zGetAgentsInviteOptionsQuery.optional() }))
.output(zGetAgentsInviteOptionsResponse)
export const inviteOptions = {
@ -66,16 +61,8 @@ export const byVersionId = {
get: get2,
}
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const get3 = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'GET',
operationId: 'getAgentsByAgentIdVersions',
@ -90,35 +77,20 @@ export const versions = {
byVersionId,
}
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const delete_ = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'DELETE',
operationId: 'deleteAgentsByAgentId',
path: '/agents/{agent_id}',
successStatus: 204,
tags: ['console'],
})
.input(z.object({ params: zDeleteAgentsByAgentIdPath }))
.output(zDeleteAgentsByAgentIdResponse)
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const get4 = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'GET',
operationId: 'getAgentsByAgentId',
@ -128,16 +100,8 @@ export const get4 = oc
.input(z.object({ params: zGetAgentsByAgentIdPath }))
.output(zGetAgentsByAgentIdResponse)
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const patch = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'PATCH',
operationId: 'patchAgentsByAgentId',
@ -154,22 +118,15 @@ export const byAgentId = {
versions,
}
/**
* Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.
*
* @deprecated
*/
export const get5 = oc
.route({
deprecated: true,
description:
'Generated contract types may be inaccurate because backend OpenAPI annotations are incomplete. Do not migrate callers until the generated contract is accurate.',
inputStructure: 'detailed',
method: 'GET',
operationId: 'getAgents',
path: '/agents',
tags: ['console'],
})
.input(z.object({ query: zGetAgentsQuery.optional() }))
.output(zGetAgentsResponse)
/**
@ -186,6 +143,7 @@ export const post = oc
method: 'POST',
operationId: 'postAgents',
path: '/agents',
successStatus: 201,
tags: ['console'],
})
.input(z.object({ body: zPostAgentsBody }))

View File

@ -4,6 +4,14 @@ export type ClientOptions = {
baseUrl: `${string}://${string}/console/api` | (string & {})
}
export type AgentRosterListResponse = {
data: Array<AgentRosterResponse>
has_more: boolean
limit: number
page: number
total: number
}
export type RosterAgentCreatePayload = {
agent_soul?: AgentSoulConfig
description?: string
@ -14,6 +22,38 @@ export type RosterAgentCreatePayload = {
version_note?: string | null
}
export type AgentRosterResponse = {
active_config_snapshot?: AgentConfigSnapshotSummaryResponse
active_config_snapshot_id?: string | null
agent_kind: AgentKind
app_id?: string | null
archived_at?: string | null
archived_by?: string | null
created_at?: string | null
created_by?: string | null
description: string
icon?: string | null
icon_background?: string | null
icon_type?: AgentIconType
id: string
name: string
scope: AgentScope
source: AgentSource
status: AgentStatus
updated_at?: string | null
updated_by?: string | null
workflow_id?: string | null
workflow_node_id?: string | null
}
export type AgentInviteOptionsResponse = {
data: Array<AgentInviteOptionResponse>
has_more: boolean
limit: number
page: number
total: number
}
export type RosterAgentUpdatePayload = {
description?: string | null
icon?: string | null
@ -22,6 +62,22 @@ export type RosterAgentUpdatePayload = {
name?: string | null
}
export type AgentConfigSnapshotListResponse = {
data: Array<AgentConfigSnapshotSummaryResponse>
}
export type AgentConfigSnapshotDetailResponse = {
agent_id?: string | null
config_snapshot: AgentSoulConfig
created_at?: string | null
created_by?: string | null
id: string
revisions?: Array<AgentConfigRevisionResponse>
summary?: string | null
version: number
version_note?: string | null
}
export type AgentSoulConfig = {
app_features?: {
[key: string]: unknown
@ -44,6 +100,63 @@ export type AgentSoulConfig = {
export type AgentIconType = 'emoji' | 'image' | 'link'
export type AgentConfigSnapshotSummaryResponse = {
agent_id?: string | null
created_at?: string | null
created_by?: string | null
id: string
summary?: string | null
version: number
version_note?: string | null
}
export type AgentKind = 'dify_agent'
export type AgentScope = 'roster' | 'workflow_only'
export type AgentSource = 'agent_app' | 'imported' | 'system' | 'workflow'
export type AgentStatus = 'active' | 'archived'
export type AgentInviteOptionResponse = {
active_config_snapshot?: AgentConfigSnapshotSummaryResponse
active_config_snapshot_id?: string | null
agent_kind: AgentKind
app_id?: string | null
archived_at?: string | null
archived_by?: string | null
created_at?: string | null
created_by?: string | null
description: string
existing_node_ids?: Array<string>
icon?: string | null
icon_background?: string | null
icon_type?: AgentIconType
id: string
in_current_workflow_count?: number
is_in_current_workflow?: boolean
name: string
scope: AgentScope
source: AgentSource
status: AgentStatus
updated_at?: string | null
updated_by?: string | null
workflow_id?: string | null
workflow_node_id?: string | null
}
export type AgentConfigRevisionResponse = {
created_at?: string | null
created_by?: string | null
current_snapshot_id: string
id: string
operation: AgentConfigRevisionOperation
previous_snapshot_id?: string | null
revision: number
summary?: string | null
version_note?: string | null
}
export type AppVariableConfig = {
default?: unknown
name: string
@ -124,6 +237,13 @@ export type AgentSoulToolsConfig = {
dify_tools?: Array<AgentSoulDifyToolConfig>
}
export type AgentConfigRevisionOperation
= | 'create_version'
| 'save_current_version'
| 'save_new_agent'
| 'save_new_version'
| 'save_to_roster'
export type AgentKnowledgeQueryMode = 'generated_query' | 'user_query'
export type AgentSoulModelCredentialRef = {
@ -157,14 +277,16 @@ export type AgentSoulDifyToolCredentialRef = {
export type GetAgentsData = {
body?: never
path?: never
query?: never
query?: {
keyword?: string
limit?: number
page?: number
}
url: '/agents'
}
export type GetAgentsResponses = {
200: {
[key: string]: unknown
}
200: AgentRosterListResponse
}
export type GetAgentsResponse = GetAgentsResponses[keyof GetAgentsResponses]
@ -177,9 +299,7 @@ export type PostAgentsData = {
}
export type PostAgentsResponses = {
200: {
[key: string]: unknown
}
201: AgentRosterResponse
}
export type PostAgentsResponse = PostAgentsResponses[keyof PostAgentsResponses]
@ -187,14 +307,17 @@ export type PostAgentsResponse = PostAgentsResponses[keyof PostAgentsResponses]
export type GetAgentsInviteOptionsData = {
body?: never
path?: never
query?: never
query?: {
app_id?: string
keyword?: string
limit?: number
page?: number
}
url: '/agents/invite-options'
}
export type GetAgentsInviteOptionsResponses = {
200: {
[key: string]: unknown
}
200: AgentInviteOptionsResponse
}
export type GetAgentsInviteOptionsResponse
@ -210,8 +333,8 @@ export type DeleteAgentsByAgentIdData = {
}
export type DeleteAgentsByAgentIdResponses = {
200: {
[key: string]: unknown
204: {
[key: string]: never
}
}
@ -228,9 +351,7 @@ export type GetAgentsByAgentIdData = {
}
export type GetAgentsByAgentIdResponses = {
200: {
[key: string]: unknown
}
200: AgentRosterResponse
}
export type GetAgentsByAgentIdResponse
@ -246,9 +367,7 @@ export type PatchAgentsByAgentIdData = {
}
export type PatchAgentsByAgentIdResponses = {
200: {
[key: string]: unknown
}
200: AgentRosterResponse
}
export type PatchAgentsByAgentIdResponse
@ -264,9 +383,7 @@ export type GetAgentsByAgentIdVersionsData = {
}
export type GetAgentsByAgentIdVersionsResponses = {
200: {
[key: string]: unknown
}
200: AgentConfigSnapshotListResponse
}
export type GetAgentsByAgentIdVersionsResponse
@ -283,9 +400,7 @@ export type GetAgentsByAgentIdVersionsByVersionIdData = {
}
export type GetAgentsByAgentIdVersionsByVersionIdResponses = {
200: {
[key: string]: unknown
}
200: AgentConfigSnapshotDetailResponse
}
export type GetAgentsByAgentIdVersionsByVersionIdResponse

View File

@ -20,6 +20,136 @@ export const zRosterAgentUpdatePayload = z.object({
name: z.string().min(1).max(255).nullish(),
})
/**
* AgentConfigSnapshotSummaryResponse
*/
export const zAgentConfigSnapshotSummaryResponse = z.object({
agent_id: z.string().nullish(),
created_at: z.string().nullish(),
created_by: z.string().nullish(),
id: z.string(),
summary: z.string().nullish(),
version: z.int(),
version_note: z.string().nullish(),
})
/**
* AgentConfigSnapshotListResponse
*/
export const zAgentConfigSnapshotListResponse = z.object({
data: z.array(zAgentConfigSnapshotSummaryResponse),
})
/**
* AgentKind
*
* Agent implementation family.
*
* This leaves room for future non-Dify agent implementations while keeping
* the current roster/workflow APIs scoped to Dify Agent.
*/
export const zAgentKind = z.enum(['dify_agent'])
/**
* AgentScope
*
* Visibility and lifecycle scope of an Agent record.
*/
export const zAgentScope = z.enum(['roster', 'workflow_only'])
/**
* AgentSource
*
* Origin that created or imported the Agent.
*/
export const zAgentSource = z.enum(['agent_app', 'imported', 'system', 'workflow'])
/**
* AgentStatus
*
* Soft lifecycle state for Agent records.
*/
export const zAgentStatus = z.enum(['active', 'archived'])
/**
* AgentRosterResponse
*/
export const zAgentRosterResponse = z.object({
active_config_snapshot: zAgentConfigSnapshotSummaryResponse.optional(),
active_config_snapshot_id: z.string().nullish(),
agent_kind: zAgentKind,
app_id: z.string().nullish(),
archived_at: z.string().nullish(),
archived_by: z.string().nullish(),
created_at: z.string().nullish(),
created_by: z.string().nullish(),
description: z.string(),
icon: z.string().nullish(),
icon_background: z.string().nullish(),
icon_type: zAgentIconType.optional(),
id: z.string(),
name: z.string(),
scope: zAgentScope,
source: zAgentSource,
status: zAgentStatus,
updated_at: z.string().nullish(),
updated_by: z.string().nullish(),
workflow_id: z.string().nullish(),
workflow_node_id: z.string().nullish(),
})
/**
* AgentRosterListResponse
*/
export const zAgentRosterListResponse = z.object({
data: z.array(zAgentRosterResponse),
has_more: z.boolean(),
limit: z.int(),
page: z.int(),
total: z.int(),
})
/**
* AgentInviteOptionResponse
*/
export const zAgentInviteOptionResponse = z.object({
active_config_snapshot: zAgentConfigSnapshotSummaryResponse.optional(),
active_config_snapshot_id: z.string().nullish(),
agent_kind: zAgentKind,
app_id: z.string().nullish(),
archived_at: z.string().nullish(),
archived_by: z.string().nullish(),
created_at: z.string().nullish(),
created_by: z.string().nullish(),
description: z.string(),
existing_node_ids: z.array(z.string()).optional(),
icon: z.string().nullish(),
icon_background: z.string().nullish(),
icon_type: zAgentIconType.optional(),
id: z.string(),
in_current_workflow_count: z.int().optional().default(0),
is_in_current_workflow: z.boolean().optional().default(false),
name: z.string(),
scope: zAgentScope,
source: zAgentSource,
status: zAgentStatus,
updated_at: z.string().nullish(),
updated_by: z.string().nullish(),
workflow_id: z.string().nullish(),
workflow_node_id: z.string().nullish(),
})
/**
* AgentInviteOptionsResponse
*/
export const zAgentInviteOptionsResponse = z.object({
data: z.array(zAgentInviteOptionResponse),
has_more: z.boolean(),
limit: z.int(),
page: z.int(),
total: z.int(),
})
/**
* AppVariableConfig
*/
@ -78,6 +208,34 @@ export const zAgentSoulSkillsFilesConfig = z.object({
skills: z.array(z.record(z.string(), z.unknown())).optional(),
})
/**
* AgentConfigRevisionOperation
*
* Audit operation recorded for Agent Soul version/revision changes.
*/
export const zAgentConfigRevisionOperation = z.enum([
'create_version',
'save_current_version',
'save_new_agent',
'save_new_version',
'save_to_roster',
])
/**
* AgentConfigRevisionResponse
*/
export const zAgentConfigRevisionResponse = z.object({
created_at: z.string().nullish(),
created_by: z.string().nullish(),
current_snapshot_id: z.string(),
id: z.string(),
operation: zAgentConfigRevisionOperation,
previous_snapshot_id: z.string().nullish(),
revision: z.int(),
summary: z.string().nullish(),
version_note: z.string().nullish(),
})
/**
* AgentKnowledgeQueryMode
*/
@ -196,39 +354,67 @@ export const zRosterAgentCreatePayload = z.object({
})
/**
* Success
* AgentConfigSnapshotDetailResponse
*/
export const zGetAgentsResponse = z.record(z.string(), z.unknown())
export const zAgentConfigSnapshotDetailResponse = z.object({
agent_id: z.string().nullish(),
config_snapshot: zAgentSoulConfig,
created_at: z.string().nullish(),
created_by: z.string().nullish(),
id: z.string(),
revisions: z.array(zAgentConfigRevisionResponse).optional(),
summary: z.string().nullish(),
version: z.int(),
version_note: z.string().nullish(),
})
export const zGetAgentsQuery = z.object({
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).optional().default(1),
})
/**
* Agent roster list
*/
export const zGetAgentsResponse = zAgentRosterListResponse
export const zPostAgentsBody = zRosterAgentCreatePayload
/**
* Success
* Agent created
*/
export const zPostAgentsResponse = z.record(z.string(), z.unknown())
export const zPostAgentsResponse = zAgentRosterResponse
export const zGetAgentsInviteOptionsQuery = z.object({
app_id: z.string().optional(),
keyword: z.string().optional(),
limit: z.int().gte(1).lte(100).optional().default(20),
page: z.int().gte(1).optional().default(1),
})
/**
* Success
* Agent invite options
*/
export const zGetAgentsInviteOptionsResponse = z.record(z.string(), z.unknown())
export const zGetAgentsInviteOptionsResponse = zAgentInviteOptionsResponse
export const zDeleteAgentsByAgentIdPath = z.object({
agent_id: z.string(),
})
/**
* Success
* Agent archived
*/
export const zDeleteAgentsByAgentIdResponse = z.record(z.string(), z.unknown())
export const zDeleteAgentsByAgentIdResponse = z.record(z.string(), z.never())
export const zGetAgentsByAgentIdPath = z.object({
agent_id: z.string(),
})
/**
* Success
* Agent detail
*/
export const zGetAgentsByAgentIdResponse = z.record(z.string(), z.unknown())
export const zGetAgentsByAgentIdResponse = zAgentRosterResponse
export const zPatchAgentsByAgentIdBody = zRosterAgentUpdatePayload
@ -237,18 +423,18 @@ export const zPatchAgentsByAgentIdPath = z.object({
})
/**
* Success
* Agent updated
*/
export const zPatchAgentsByAgentIdResponse = z.record(z.string(), z.unknown())
export const zPatchAgentsByAgentIdResponse = zAgentRosterResponse
export const zGetAgentsByAgentIdVersionsPath = z.object({
agent_id: z.string(),
})
/**
* Success
* Agent versions
*/
export const zGetAgentsByAgentIdVersionsResponse = z.record(z.string(), z.unknown())
export const zGetAgentsByAgentIdVersionsResponse = zAgentConfigSnapshotListResponse
export const zGetAgentsByAgentIdVersionsByVersionIdPath = z.object({
agent_id: z.string(),
@ -256,6 +442,6 @@ export const zGetAgentsByAgentIdVersionsByVersionIdPath = z.object({
})
/**
* Success
* Agent version detail
*/
export const zGetAgentsByAgentIdVersionsByVersionIdResponse = z.record(z.string(), z.unknown())
export const zGetAgentsByAgentIdVersionsByVersionIdResponse = zAgentConfigSnapshotDetailResponse

View File

@ -352,6 +352,7 @@ import {
zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunBody,
zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunPath,
zPostAppsByAppIdWorkflowsDraftLoopNodesByNodeIdRunResponse,
zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactBody,
zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactPath,
zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse,
zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterBody,
@ -3516,7 +3517,12 @@ export const post47 = oc
path: '/apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/impact',
tags: ['console'],
})
.input(z.object({ params: zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactPath }))
.input(
z.object({
body: zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactBody,
params: zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactPath,
}),
)
.output(zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse)
export const impact = {

View File

@ -167,6 +167,14 @@ export type AdvancedChatWorkflowRunPayload = {
query?: string
}
export type AgentAppComposerResponse = {
active_config_snapshot: AgentConfigSnapshotSummaryResponse
agent: AgentComposerAgentResponse
agent_soul: AgentSoulConfig
save_options: Array<ComposerSaveStrategy>
variant: string
}
export type ComposerSavePayload = {
agent_soul?: AgentSoulConfig
binding?: ComposerBindingPayload
@ -180,6 +188,18 @@ export type ComposerSavePayload = {
version_note?: string | null
}
export type AgentComposerCandidatesResponse = {
allowed_node_job_candidates?: AgentComposerNodeJobCandidatesResponse
allowed_soul_candidates?: AgentComposerSoulCandidatesResponse
capabilities?: ComposerCandidateCapabilities
variant: ComposerVariant
}
export type AgentComposerValidateResponse = {
errors?: Array<string>
result: string
}
export type AnnotationReplyPayload = {
embedding_model_name: string
embedding_provider_name: string
@ -736,6 +756,28 @@ export type HumanInputDeliveryTestPayload = {
}
}
export type WorkflowAgentComposerResponse = {
active_config_snapshot?: AgentConfigSnapshotSummaryResponse
agent?: AgentComposerAgentResponse
agent_soul: AgentSoulConfig
app_id?: string | null
binding?: AgentComposerBindingResponse
effective_declared_outputs?: Array<DeclaredOutputConfig>
impact_summary?: AgentComposerImpactResponse
node_id?: string | null
node_job: WorkflowNodeJobConfig
save_options: Array<ComposerSaveStrategy>
soul_lock: AgentComposerSoulLockResponse
variant: string
workflow_id?: string | null
}
export type AgentComposerImpactResponse = {
bindings?: Array<AgentComposerImpactBindingResponse>
current_snapshot_id?: string | null
workflow_node_count: number
}
export type WorkflowRunNodeExecutionResponse = {
created_at?: number | null
created_by_account?: SimpleAccount
@ -961,6 +1003,25 @@ export type AdvancedChatWorkflowRunForListResponse = {
version?: string | null
}
export type AgentConfigSnapshotSummaryResponse = {
agent_id?: string | null
created_at?: string | null
created_by?: string | null
id: string
summary?: string | null
version: number
version_note?: string | null
}
export type AgentComposerAgentResponse = {
active_config_snapshot_id?: string | null
description: string
id: string
name: string
scope: AgentScope
status: AgentStatus
}
export type AgentSoulConfig = {
app_features?: {
[key: string]: unknown
@ -981,6 +1042,13 @@ export type AgentSoulConfig = {
tools?: AgentSoulToolsConfig
}
export type ComposerSaveStrategy
= | 'node_job_only'
| 'save_as_new_agent'
| 'save_as_new_version'
| 'save_to_current_version'
| 'save_to_roster'
export type ComposerBindingPayload = {
agent_id?: string | null
binding_type: 'inline_agent' | 'roster_agent'
@ -1003,13 +1071,6 @@ export type WorkflowNodeJobConfig = {
workflow_prompt?: string
}
export type ComposerSaveStrategy
= | 'node_job_only'
| 'save_as_new_agent'
| 'save_as_new_version'
| 'save_to_current_version'
| 'save_to_roster'
export type ComposerSoulLockPayload = {
locked?: boolean
unlocked_from_version_id?: string | null
@ -1017,6 +1078,38 @@ export type ComposerSoulLockPayload = {
export type ComposerVariant = 'agent_app' | 'workflow'
export type AgentComposerNodeJobCandidatesResponse = {
declare_output_types?: Array<DeclaredOutputType>
human_contacts?: Array<{
[key: string]: unknown
}>
previous_node_outputs?: Array<{
[key: string]: unknown
}>
}
export type AgentComposerSoulCandidatesResponse = {
cli_tools?: Array<{
[key: string]: unknown
}>
dify_tools?: Array<{
[key: string]: unknown
}>
human_contacts?: Array<{
[key: string]: unknown
}>
knowledge_datasets?: Array<{
[key: string]: unknown
}>
skills_files?: Array<{
[key: string]: unknown
}>
}
export type ComposerCandidateCapabilities = {
human_roster_available?: boolean
}
export type AnnotationHitHistory = {
annotation_content?: string | null
annotation_question?: string | null
@ -1305,6 +1398,39 @@ export type PipelineVariableResponse = {
variable: string
}
export type AgentComposerBindingResponse = {
agent_id?: string | null
binding_type: WorkflowAgentBindingType
current_snapshot_id?: string | null
id: string
node_id: string
workflow_id: string
}
export type DeclaredOutputConfig = {
array_item?: DeclaredArrayItem
check?: DeclaredOutputCheckConfig
description?: string | null
failure_strategy?: DeclaredOutputFailureStrategy
file?: DeclaredOutputFileConfig
id?: string | null
name: string
required?: boolean
type: DeclaredOutputType
}
export type AgentComposerSoulLockResponse = {
can_unlock?: boolean
locked: boolean
reason?: string | null
}
export type AgentComposerImpactBindingResponse = {
app_id: string
node_id: string
workflow_id: string
}
export type WorkflowDraftVariableWithoutValue = {
description?: string
edited?: boolean
@ -1353,6 +1479,10 @@ export type WorkflowOnlineUser = {
username: string
}
export type AgentScope = 'roster' | 'workflow_only'
export type AgentStatus = 'active' | 'archived'
export type AppVariableConfig = {
default?: unknown
name: string
@ -1433,20 +1563,10 @@ export type AgentSoulToolsConfig = {
dify_tools?: Array<AgentSoulDifyToolConfig>
}
export type DeclaredOutputConfig = {
array_item?: DeclaredArrayItem
check?: DeclaredOutputCheckConfig
description?: string | null
failure_strategy?: DeclaredOutputFailureStrategy
file?: DeclaredOutputFileConfig
id?: string | null
name: string
required?: boolean
type: DeclaredOutputType
}
export type WorkflowNodeJobMode = 'let_agent_figure_it_out' | 'tell_agent_what_to_do'
export type DeclaredOutputType = 'array' | 'boolean' | 'file' | 'number' | 'object' | 'string'
export type SimpleModelConfig = {
model_dict?: JsonValue
pre_prompt?: string | null
@ -1515,29 +1635,7 @@ export type WorkflowRunForArchivedLogResponse = {
triggered_from?: string | null
}
export type AgentKnowledgeQueryMode = 'generated_query' | 'user_query'
export type AgentSoulModelCredentialRef = {
id?: string | null
provider?: string | null
type: string
}
export type AgentSoulDifyToolConfig = {
credential_ref?: AgentSoulDifyToolCredentialRef
credential_type?: 'api-key' | 'oauth2' | 'unauthorized'
description?: string | null
enabled?: boolean
name?: string | null
plugin_id?: string | null
provider?: string | null
provider_id?: string | null
provider_type?: string
runtime_parameters?: {
[key: string]: unknown
}
tool_name: string
}
export type WorkflowAgentBindingType = 'inline_agent' | 'roster_agent'
export type DeclaredArrayItem = {
description?: string | null
@ -1566,7 +1664,29 @@ export type DeclaredOutputFileConfig = {
mime_types?: Array<string>
}
export type DeclaredOutputType = 'array' | 'boolean' | 'file' | 'number' | 'object' | 'string'
export type AgentKnowledgeQueryMode = 'generated_query' | 'user_query'
export type AgentSoulModelCredentialRef = {
id?: string | null
provider?: string | null
type: string
}
export type AgentSoulDifyToolConfig = {
credential_ref?: AgentSoulDifyToolCredentialRef
credential_type?: 'api-key' | 'oauth2' | 'unauthorized'
description?: string | null
enabled?: boolean
name?: string | null
plugin_id?: string | null
provider?: string | null
provider_id?: string | null
provider_type?: string
runtime_parameters?: {
[key: string]: unknown
}
tool_name: string
}
export type UserActionConfig = {
button_style?: ButtonStyle
@ -1576,12 +1696,6 @@ export type UserActionConfig = {
export type FormInputConfig = unknown
export type AgentSoulDifyToolCredentialRef = {
id?: string | null
provider?: string | null
type?: 'provider' | 'tool'
}
export type OutputErrorStrategy = 'default_value' | 'fail_branch' | 'stop'
export type DeclaredOutputRetryConfig = {
@ -1590,6 +1704,12 @@ export type DeclaredOutputRetryConfig = {
retry_interval_ms?: number
}
export type AgentSoulDifyToolCredentialRef = {
id?: string | null
provider?: string | null
type?: 'provider' | 'tool'
}
export type ButtonStyle = 'accent' | 'default' | 'ghost' | 'primary'
export type ParagraphInputConfig = {
@ -2062,9 +2182,7 @@ export type GetAppsByAppIdAgentComposerData = {
}
export type GetAppsByAppIdAgentComposerResponses = {
200: {
[key: string]: unknown
}
200: AgentAppComposerResponse
}
export type GetAppsByAppIdAgentComposerResponse
@ -2080,9 +2198,7 @@ export type PutAppsByAppIdAgentComposerData = {
}
export type PutAppsByAppIdAgentComposerResponses = {
200: {
[key: string]: unknown
}
200: AgentAppComposerResponse
}
export type PutAppsByAppIdAgentComposerResponse
@ -2098,9 +2214,7 @@ export type GetAppsByAppIdAgentComposerCandidatesData = {
}
export type GetAppsByAppIdAgentComposerCandidatesResponses = {
200: {
[key: string]: unknown
}
200: AgentComposerCandidatesResponse
}
export type GetAppsByAppIdAgentComposerCandidatesResponse
@ -2116,9 +2230,7 @@ export type PostAppsByAppIdAgentComposerValidateData = {
}
export type PostAppsByAppIdAgentComposerValidateResponses = {
200: {
[key: string]: unknown
}
200: AgentComposerValidateResponse
}
export type PostAppsByAppIdAgentComposerValidateResponse
@ -4515,9 +4627,7 @@ export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerData = {
}
export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponses = {
200: {
[key: string]: unknown
}
200: WorkflowAgentComposerResponse
}
export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse
@ -4534,9 +4644,7 @@ export type PutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerData = {
}
export type PutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponses = {
200: {
[key: string]: unknown
}
200: WorkflowAgentComposerResponse
}
export type PutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse
@ -4553,16 +4661,14 @@ export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesData
}
export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponses = {
200: {
[key: string]: unknown
}
200: AgentComposerCandidatesResponse
}
export type GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponse
= GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponses[keyof GetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponses]
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactData = {
body?: never
body: ComposerSavePayload
path: {
app_id: string
node_id: string
@ -4572,9 +4678,7 @@ export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactData =
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponses = {
200: {
[key: string]: unknown
}
200: AgentComposerImpactResponse
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse
@ -4591,9 +4695,7 @@ export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterD
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterResponses = {
200: {
[key: string]: unknown
}
200: WorkflowAgentComposerResponse
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterResponse
@ -4610,9 +4712,7 @@ export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateData
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateResponses = {
200: {
[key: string]: unknown
}
200: AgentComposerValidateResponse
}
export type PostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateResponse

View File

@ -77,6 +77,14 @@ export const zAdvancedChatWorkflowRunPayload = z.object({
query: z.string().optional().default(''),
})
/**
* AgentComposerValidateResponse
*/
export const zAgentComposerValidateResponse = z.object({
errors: z.array(z.string()).optional(),
result: z.string(),
})
/**
* AnnotationReplyPayload
*/
@ -730,12 +738,16 @@ export const zSite = z.object({
})
/**
* ComposerBindingPayload
* AgentConfigSnapshotSummaryResponse
*/
export const zComposerBindingPayload = z.object({
export const zAgentConfigSnapshotSummaryResponse = z.object({
agent_id: z.string().nullish(),
binding_type: z.enum(['inline_agent', 'roster_agent']),
current_snapshot_id: z.string().nullish(),
created_at: z.string().nullish(),
created_by: z.string().nullish(),
id: z.string(),
summary: z.string().nullish(),
version: z.int(),
version_note: z.string().nullish(),
})
/**
@ -749,6 +761,15 @@ export const zComposerSaveStrategy = z.enum([
'save_to_roster',
])
/**
* ComposerBindingPayload
*/
export const zComposerBindingPayload = z.object({
agent_id: z.string().nullish(),
binding_type: z.enum(['inline_agent', 'roster_agent']),
current_snapshot_id: z.string().nullish(),
})
/**
* ComposerSoulLockPayload
*/
@ -762,6 +783,24 @@ export const zComposerSoulLockPayload = z.object({
*/
export const zComposerVariant = z.enum(['agent_app', 'workflow'])
/**
* AgentComposerSoulCandidatesResponse
*/
export const zAgentComposerSoulCandidatesResponse = z.object({
cli_tools: z.array(z.record(z.string(), z.unknown())).optional(),
dify_tools: z.array(z.record(z.string(), z.unknown())).optional(),
human_contacts: z.array(z.record(z.string(), z.unknown())).optional(),
knowledge_datasets: z.array(z.record(z.string(), z.unknown())).optional(),
skills_files: z.array(z.record(z.string(), z.unknown())).optional(),
})
/**
* ComposerCandidateCapabilities
*/
export const zComposerCandidateCapabilities = z.object({
human_roster_available: z.boolean().optional().default(false),
})
/**
* AnnotationHitHistory
*/
@ -1235,6 +1274,33 @@ export const zWorkflowPaginationResponse = z.object({
page: z.int(),
})
/**
* AgentComposerSoulLockResponse
*/
export const zAgentComposerSoulLockResponse = z.object({
can_unlock: z.boolean().optional().default(false),
locked: z.boolean(),
reason: z.string().nullish(),
})
/**
* AgentComposerImpactBindingResponse
*/
export const zAgentComposerImpactBindingResponse = z.object({
app_id: z.string(),
node_id: z.string(),
workflow_id: z.string(),
})
/**
* AgentComposerImpactResponse
*/
export const zAgentComposerImpactResponse = z.object({
bindings: z.array(zAgentComposerImpactBindingResponse).optional(),
current_snapshot_id: z.string().nullish(),
workflow_node_count: z.int(),
})
export const zWorkflowDraftVariableWithoutValue = z.object({
description: z.string().optional(),
edited: z.boolean().optional(),
@ -1475,6 +1541,32 @@ export const zWorkflowOnlineUsersResponse = z.object({
data: z.array(zWorkflowOnlineUsersByApp),
})
/**
* AgentScope
*
* Visibility and lifecycle scope of an Agent record.
*/
export const zAgentScope = z.enum(['roster', 'workflow_only'])
/**
* AgentStatus
*
* Soft lifecycle state for Agent records.
*/
export const zAgentStatus = z.enum(['active', 'archived'])
/**
* AgentComposerAgentResponse
*/
export const zAgentComposerAgentResponse = z.object({
active_config_snapshot_id: z.string().nullish(),
description: z.string(),
id: z.string(),
name: z.string(),
scope: zAgentScope,
status: zAgentStatus,
})
/**
* AppVariableConfig
*/
@ -1538,6 +1630,37 @@ export const zAgentSoulSkillsFilesConfig = z.object({
*/
export const zWorkflowNodeJobMode = z.enum(['let_agent_figure_it_out', 'tell_agent_what_to_do'])
/**
* DeclaredOutputType
*/
export const zDeclaredOutputType = z.enum([
'array',
'boolean',
'file',
'number',
'object',
'string',
])
/**
* AgentComposerNodeJobCandidatesResponse
*/
export const zAgentComposerNodeJobCandidatesResponse = z.object({
declare_output_types: z.array(zDeclaredOutputType).optional(),
human_contacts: z.array(z.record(z.string(), z.unknown())).optional(),
previous_node_outputs: z.array(z.record(z.string(), z.unknown())).optional(),
})
/**
* AgentComposerCandidatesResponse
*/
export const zAgentComposerCandidatesResponse = z.object({
allowed_node_job_candidates: zAgentComposerNodeJobCandidatesResponse.optional(),
allowed_soul_candidates: zAgentComposerSoulCandidatesResponse.optional(),
capabilities: zComposerCandidateCapabilities.optional(),
variant: zComposerVariant,
})
/**
* SimpleModelConfig
*/
@ -1725,6 +1848,63 @@ export const zWorkflowArchivedLogPaginationResponse = z.object({
total: z.int(),
})
/**
* WorkflowAgentBindingType
*
* How a workflow node is bound to an Agent.
*/
export const zWorkflowAgentBindingType = z.enum(['inline_agent', 'roster_agent'])
/**
* AgentComposerBindingResponse
*/
export const zAgentComposerBindingResponse = z.object({
agent_id: z.string().nullish(),
binding_type: zWorkflowAgentBindingType,
current_snapshot_id: z.string().nullish(),
id: z.string(),
node_id: z.string(),
workflow_id: z.string(),
})
/**
* DeclaredArrayItem
*
* Per-item shape for an ``array``-typed declared output.
*
* PRD §OUTPUT keeps arrays one level deep on first version; nested arrays
* are rejected so the runtime type checker and JSON Schema stay easy to reason
* about. Stage 4 §4.2.
*/
export const zDeclaredArrayItem = z.object({
description: z.string().nullish(),
type: zDeclaredOutputType,
})
/**
* DeclaredOutputCheckConfig
*
* File-output content check via a model-based comparison against a benchmark file.
*
* Per PRD §OUTPUT , output check is **file-only** and optional. Stage 4 §4.3.
*/
export const zDeclaredOutputCheckConfig = z.object({
benchmark_file_ref: z.record(z.string(), z.unknown()).nullish(),
enabled: z.boolean().optional().default(false),
model_ref: z.record(z.string(), z.unknown()).nullish(),
prompt: z.string().nullish(),
})
/**
* DeclaredOutputFileConfig
*
* File-type output metadata. Both lists empty means "any file accepted".
*/
export const zDeclaredOutputFileConfig = z.object({
extensions: z.array(z.string()).optional(),
mime_types: z.array(z.string()).optional(),
})
/**
* AgentKnowledgeQueryMode
*/
@ -1763,124 +1943,8 @@ export const zAgentSoulModelConfig = z.object({
plugin_id: z.string().min(1).max(255),
})
/**
* DeclaredOutputCheckConfig
*
* File-output content check via a model-based comparison against a benchmark file.
*
* Per PRD §OUTPUT , output check is **file-only** and optional. Stage 4 §4.3.
*/
export const zDeclaredOutputCheckConfig = z.object({
benchmark_file_ref: z.record(z.string(), z.unknown()).nullish(),
enabled: z.boolean().optional().default(false),
model_ref: z.record(z.string(), z.unknown()).nullish(),
prompt: z.string().nullish(),
})
/**
* DeclaredOutputFileConfig
*
* File-type output metadata. Both lists empty means "any file accepted".
*/
export const zDeclaredOutputFileConfig = z.object({
extensions: z.array(z.string()).optional(),
mime_types: z.array(z.string()).optional(),
})
/**
* DeclaredOutputType
*/
export const zDeclaredOutputType = z.enum([
'array',
'boolean',
'file',
'number',
'object',
'string',
])
/**
* DeclaredArrayItem
*
* Per-item shape for an ``array``-typed declared output.
*
* PRD §OUTPUT keeps arrays one level deep on first version; nested arrays
* are rejected so the runtime type checker and JSON Schema stay easy to reason
* about. Stage 4 §4.2.
*/
export const zDeclaredArrayItem = z.object({
description: z.string().nullish(),
type: zDeclaredOutputType,
})
export const zFormInputConfig = z.unknown()
/**
* AgentSoulDifyToolCredentialRef
*
* Reference to a stored Dify Plugin Tool credential.
*
* Secret values are resolved only at runtime. The legacy ``credential_id``
* field is accepted by :class:`AgentSoulDifyToolConfig` and normalized here so
* old Agent tool payloads can be read while new payloads stay explicit.
*/
export const zAgentSoulDifyToolCredentialRef = z.object({
id: z.string().max(255).nullish(),
provider: z.string().max(255).nullish(),
type: z.enum(['provider', 'tool']).optional().default('tool'),
})
/**
* AgentSoulDifyToolConfig
*
* One Dify Plugin Tool configured on Agent Soul.
*
* The API backend prepares this persisted product shape into
* ``DifyPluginToolConfig`` before sending a run request to Agent backend.
* ``provider_id`` keeps compatibility with existing Agent tool config payloads;
* new callers should send ``plugin_id`` + ``provider`` when available.
*/
export const zAgentSoulDifyToolConfig = z.object({
credential_ref: zAgentSoulDifyToolCredentialRef.optional(),
credential_type: z.enum(['api-key', 'oauth2', 'unauthorized']).optional().default('api-key'),
description: z.string().nullish(),
enabled: z.boolean().optional().default(true),
name: z.string().max(255).nullish(),
plugin_id: z.string().max(255).nullish(),
provider: z.string().max(255).nullish(),
provider_id: z.string().max(255).nullish(),
provider_type: z.string().optional().default('plugin'),
runtime_parameters: z.record(z.string(), z.unknown()).optional(),
tool_name: z.string().min(1).max(255),
})
/**
* AgentSoulToolsConfig
*/
export const zAgentSoulToolsConfig = z.object({
cli_tools: z.array(z.record(z.string(), z.unknown())).optional(),
dify_tools: z.array(zAgentSoulDifyToolConfig).optional(),
})
/**
* AgentSoulConfig
*/
export const zAgentSoulConfig = z.object({
app_features: z.record(z.string(), z.unknown()).optional(),
app_variables: z.array(zAppVariableConfig).optional(),
env: zAgentSoulEnvConfig.optional(),
human: zAgentSoulHumanConfig.optional(),
knowledge: zAgentSoulKnowledgeConfig.optional(),
memory: zAgentSoulMemoryConfig.optional(),
misc_legacy: z.record(z.string(), z.unknown()).optional(),
model: zAgentSoulModelConfig.optional(),
prompt: zAgentSoulPromptConfig.optional(),
sandbox: zAgentSoulSandboxConfig.optional(),
schema_version: z.int().optional().default(1),
skills_files: zAgentSoulSkillsFilesConfig.optional(),
tools: zAgentSoulToolsConfig.optional(),
})
/**
* OutputErrorStrategy
*
@ -1951,6 +2015,83 @@ export const zWorkflowNodeJobConfig = z.object({
workflow_prompt: z.string().optional().default(''),
})
/**
* AgentSoulDifyToolCredentialRef
*
* Reference to a stored Dify Plugin Tool credential.
*
* Secret values are resolved only at runtime. The legacy ``credential_id``
* field is accepted by :class:`AgentSoulDifyToolConfig` and normalized here so
* old Agent tool payloads can be read while new payloads stay explicit.
*/
export const zAgentSoulDifyToolCredentialRef = z.object({
id: z.string().max(255).nullish(),
provider: z.string().max(255).nullish(),
type: z.enum(['provider', 'tool']).optional().default('tool'),
})
/**
* AgentSoulDifyToolConfig
*
* One Dify Plugin Tool configured on Agent Soul.
*
* The API backend prepares this persisted product shape into
* ``DifyPluginToolConfig`` before sending a run request to Agent backend.
* ``provider_id`` keeps compatibility with existing Agent tool config payloads;
* new callers should send ``plugin_id`` + ``provider`` when available.
*/
export const zAgentSoulDifyToolConfig = z.object({
credential_ref: zAgentSoulDifyToolCredentialRef.optional(),
credential_type: z.enum(['api-key', 'oauth2', 'unauthorized']).optional().default('api-key'),
description: z.string().nullish(),
enabled: z.boolean().optional().default(true),
name: z.string().max(255).nullish(),
plugin_id: z.string().max(255).nullish(),
provider: z.string().max(255).nullish(),
provider_id: z.string().max(255).nullish(),
provider_type: z.string().optional().default('plugin'),
runtime_parameters: z.record(z.string(), z.unknown()).optional(),
tool_name: z.string().min(1).max(255),
})
/**
* AgentSoulToolsConfig
*/
export const zAgentSoulToolsConfig = z.object({
cli_tools: z.array(z.record(z.string(), z.unknown())).optional(),
dify_tools: z.array(zAgentSoulDifyToolConfig).optional(),
})
/**
* AgentSoulConfig
*/
export const zAgentSoulConfig = z.object({
app_features: z.record(z.string(), z.unknown()).optional(),
app_variables: z.array(zAppVariableConfig).optional(),
env: zAgentSoulEnvConfig.optional(),
human: zAgentSoulHumanConfig.optional(),
knowledge: zAgentSoulKnowledgeConfig.optional(),
memory: zAgentSoulMemoryConfig.optional(),
misc_legacy: z.record(z.string(), z.unknown()).optional(),
model: zAgentSoulModelConfig.optional(),
prompt: zAgentSoulPromptConfig.optional(),
sandbox: zAgentSoulSandboxConfig.optional(),
schema_version: z.int().optional().default(1),
skills_files: zAgentSoulSkillsFilesConfig.optional(),
tools: zAgentSoulToolsConfig.optional(),
})
/**
* AgentAppComposerResponse
*/
export const zAgentAppComposerResponse = z.object({
active_config_snapshot: zAgentConfigSnapshotSummaryResponse,
agent: zAgentComposerAgentResponse,
agent_soul: zAgentSoulConfig,
save_options: z.array(zComposerSaveStrategy),
variant: z.string(),
})
/**
* ComposerSavePayload
*/
@ -1967,6 +2108,25 @@ export const zComposerSavePayload = z.object({
version_note: z.string().nullish(),
})
/**
* WorkflowAgentComposerResponse
*/
export const zWorkflowAgentComposerResponse = z.object({
active_config_snapshot: zAgentConfigSnapshotSummaryResponse.optional(),
agent: zAgentComposerAgentResponse.optional(),
agent_soul: zAgentSoulConfig,
app_id: z.string().nullish(),
binding: zAgentComposerBindingResponse.optional(),
effective_declared_outputs: z.array(zDeclaredOutputConfig).optional(),
impact_summary: zAgentComposerImpactResponse.optional(),
node_id: z.string().nullish(),
node_job: zWorkflowNodeJobConfig,
save_options: z.array(zComposerSaveStrategy),
soul_lock: zAgentComposerSoulLockResponse,
variant: z.string(),
workflow_id: z.string().nullish(),
})
/**
* ButtonStyle
*
@ -2412,9 +2572,9 @@ export const zGetAppsByAppIdAgentComposerPath = z.object({
})
/**
* Success
* Agent app composer state
*/
export const zGetAppsByAppIdAgentComposerResponse = z.record(z.string(), z.unknown())
export const zGetAppsByAppIdAgentComposerResponse = zAgentAppComposerResponse
export const zPutAppsByAppIdAgentComposerBody = zComposerSavePayload
@ -2423,18 +2583,18 @@ export const zPutAppsByAppIdAgentComposerPath = z.object({
})
/**
* Success
* Agent app composer saved
*/
export const zPutAppsByAppIdAgentComposerResponse = z.record(z.string(), z.unknown())
export const zPutAppsByAppIdAgentComposerResponse = zAgentAppComposerResponse
export const zGetAppsByAppIdAgentComposerCandidatesPath = z.object({
app_id: z.string(),
})
/**
* Success
* Agent app composer candidates
*/
export const zGetAppsByAppIdAgentComposerCandidatesResponse = z.record(z.string(), z.unknown())
export const zGetAppsByAppIdAgentComposerCandidatesResponse = zAgentComposerCandidatesResponse
export const zPostAppsByAppIdAgentComposerValidateBody = zComposerSavePayload
@ -2443,9 +2603,9 @@ export const zPostAppsByAppIdAgentComposerValidatePath = z.object({
})
/**
* Success
* Agent app composer validation result
*/
export const zPostAppsByAppIdAgentComposerValidateResponse = z.record(z.string(), z.unknown())
export const zPostAppsByAppIdAgentComposerValidateResponse = zAgentComposerValidateResponse
export const zGetAppsByAppIdAgentLogsPath = z.object({
app_id: z.string(),
@ -3723,12 +3883,10 @@ export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerPath = z.obj
})
/**
* Success
* Workflow agent composer state
*/
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse = z.record(
z.string(),
z.unknown(),
)
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse
= zWorkflowAgentComposerResponse
export const zPutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerBody = zComposerSavePayload
@ -3738,12 +3896,10 @@ export const zPutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerPath = z.obj
})
/**
* Success
* Workflow agent composer saved
*/
export const zPutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse = z.record(
z.string(),
z.unknown(),
)
export const zPutAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerResponse
= zWorkflowAgentComposerResponse
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesPath = z.object({
app_id: z.string(),
@ -3751,12 +3907,13 @@ export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesPa
})
/**
* Success
* Workflow agent composer candidates
*/
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponse = z.record(
z.string(),
z.unknown(),
)
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerCandidatesResponse
= zAgentComposerCandidatesResponse
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactBody
= zComposerSavePayload
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactPath = z.object({
app_id: z.string(),
@ -3764,12 +3921,10 @@ export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactPath
})
/**
* Success
* Workflow agent composer impact
*/
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse = z.record(
z.string(),
z.unknown(),
)
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerImpactResponse
= zAgentComposerImpactResponse
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterBody
= zComposerSavePayload
@ -3780,10 +3935,10 @@ export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRoste
})
/**
* Success
* Workflow agent composer saved to roster
*/
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerSaveToRosterResponse
= z.record(z.string(), z.unknown())
= zWorkflowAgentComposerResponse
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateBody
= zComposerSavePayload
@ -3794,12 +3949,10 @@ export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidatePat
})
/**
* Success
* Workflow agent composer validation result
*/
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateResponse = z.record(
z.string(),
z.unknown(),
)
export const zPostAppsByAppIdWorkflowsDraftNodesByNodeIdAgentComposerValidateResponse
= zAgentComposerValidateResponse
export const zGetAppsByAppIdWorkflowsDraftNodesByNodeIdLastRunPath = z.object({
app_id: z.string(),