mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
feat(api): enable Agent mode in workflow/service APIs and add default config (Phase 7)
Ensure new Agent apps (AppMode.AGENT) can access all workflow-related APIs and Service API chat endpoints: - Add AppMode.AGENT to 13 workflow controller mode checks - Add AppMode.AGENT to 4 workflow_run controller mode checks - Add AppMode.AGENT to workflow_draft_variable controller - Add AppMode.AGENT to Service API chat, conversation, message endpoints - Add AgentV2Node.get_default_config() with prompt templates and strategy defaults - 46 unit tests all passing (8 new Phase 7 tests) Old agent/agent-chat paths remain completely unchanged. Made-with: Cursor
This commit is contained in:
parent
96641a93f6
commit
8f3a3ea03e
@ -221,7 +221,7 @@ class DraftWorkflowApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_model)
|
@marshal_with(workflow_model)
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
@ -241,7 +241,7 @@ class DraftWorkflowApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@console_ns.doc("sync_draft_workflow")
|
@console_ns.doc("sync_draft_workflow")
|
||||||
@console_ns.doc(description="Sync draft workflow configuration")
|
@console_ns.doc(description="Sync draft workflow configuration")
|
||||||
@console_ns.expect(console_ns.models[SyncDraftWorkflowPayload.__name__])
|
@console_ns.expect(console_ns.models[SyncDraftWorkflowPayload.__name__])
|
||||||
@ -733,7 +733,7 @@ class WorkflowTaskStopApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def post(self, app_model: App, task_id: str):
|
def post(self, app_model: App, task_id: str):
|
||||||
"""
|
"""
|
||||||
@ -761,7 +761,7 @@ class DraftWorkflowNodeRunApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_node_execution_model)
|
@marshal_with(workflow_run_node_execution_model)
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def post(self, app_model: App, node_id: str):
|
def post(self, app_model: App, node_id: str):
|
||||||
@ -807,7 +807,7 @@ class PublishedWorkflowApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_model)
|
@marshal_with(workflow_model)
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
@ -825,7 +825,7 @@ class PublishedWorkflowApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def post(self, app_model: App):
|
def post(self, app_model: App):
|
||||||
"""
|
"""
|
||||||
@ -869,7 +869,7 @@ class DefaultBlockConfigsApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
"""
|
"""
|
||||||
@ -891,7 +891,7 @@ class DefaultBlockConfigApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def get(self, app_model: App, block_type: str):
|
def get(self, app_model: App, block_type: str):
|
||||||
"""
|
"""
|
||||||
@ -956,7 +956,7 @@ class PublishedAllWorkflowApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_pagination_model)
|
@marshal_with(workflow_pagination_model)
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
@ -1005,7 +1005,7 @@ class DraftWorkflowRestoreApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def post(self, app_model: App, workflow_id: str):
|
def post(self, app_model: App, workflow_id: str):
|
||||||
current_user, _ = current_account_with_tenant()
|
current_user, _ = current_account_with_tenant()
|
||||||
@ -1043,7 +1043,7 @@ class WorkflowByIdApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_model)
|
@marshal_with(workflow_model)
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def patch(self, app_model: App, workflow_id: str):
|
def patch(self, app_model: App, workflow_id: str):
|
||||||
@ -1083,7 +1083,7 @@ class WorkflowByIdApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
def delete(self, app_model: App, workflow_id: str):
|
def delete(self, app_model: App, workflow_id: str):
|
||||||
"""
|
"""
|
||||||
@ -1118,7 +1118,7 @@ class DraftWorkflowNodeLastRunApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_node_execution_model)
|
@marshal_with(workflow_run_node_execution_model)
|
||||||
def get(self, app_model: App, node_id: str):
|
def get(self, app_model: App, node_id: str):
|
||||||
srv = WorkflowService()
|
srv = WorkflowService()
|
||||||
|
|||||||
@ -208,7 +208,7 @@ def _api_prerequisite[**P, R](f: Callable[P, R]) -> Callable[P, R | Response]:
|
|||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@edit_permission_required
|
@edit_permission_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | Response:
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | Response:
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|||||||
@ -349,7 +349,7 @@ class WorkflowRunListApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_pagination_model)
|
@marshal_with(workflow_run_pagination_model)
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
"""
|
"""
|
||||||
@ -397,7 +397,7 @@ class WorkflowRunCountApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_count_model)
|
@marshal_with(workflow_run_count_model)
|
||||||
def get(self, app_model: App):
|
def get(self, app_model: App):
|
||||||
"""
|
"""
|
||||||
@ -434,7 +434,7 @@ class WorkflowRunDetailApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_detail_model)
|
@marshal_with(workflow_run_detail_model)
|
||||||
def get(self, app_model: App, run_id):
|
def get(self, app_model: App, run_id):
|
||||||
"""
|
"""
|
||||||
@ -458,7 +458,7 @@ class WorkflowRunNodeExecutionListApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
@get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW, AppMode.AGENT])
|
||||||
@marshal_with(workflow_run_node_execution_list_model)
|
@marshal_with(workflow_run_node_execution_list_model)
|
||||||
def get(self, app_model: App, run_id):
|
def get(self, app_model: App, run_id):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -194,7 +194,7 @@ class ChatApi(Resource):
|
|||||||
Supports conversation management and both blocking and streaming response modes.
|
Supports conversation management and both blocking and streaming response modes.
|
||||||
"""
|
"""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
payload = ChatRequestPayload.model_validate(service_api_ns.payload or {})
|
payload = ChatRequestPayload.model_validate(service_api_ns.payload or {})
|
||||||
@ -258,7 +258,7 @@ class ChatStopApi(Resource):
|
|||||||
def post(self, app_model: App, end_user: EndUser, task_id: str):
|
def post(self, app_model: App, end_user: EndUser, task_id: str):
|
||||||
"""Stop a running chat message generation."""
|
"""Stop a running chat message generation."""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
AppTaskService.stop_task(
|
AppTaskService.stop_task(
|
||||||
|
|||||||
@ -109,7 +109,7 @@ class ConversationApi(Resource):
|
|||||||
Supports pagination using last_id and limit parameters.
|
Supports pagination using last_id and limit parameters.
|
||||||
"""
|
"""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
query_args = ConversationListQuery.model_validate(request.args.to_dict())
|
query_args = ConversationListQuery.model_validate(request.args.to_dict())
|
||||||
@ -153,7 +153,7 @@ class ConversationDetailApi(Resource):
|
|||||||
def delete(self, app_model: App, end_user: EndUser, c_id):
|
def delete(self, app_model: App, end_user: EndUser, c_id):
|
||||||
"""Delete a specific conversation."""
|
"""Delete a specific conversation."""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
conversation_id = str(c_id)
|
conversation_id = str(c_id)
|
||||||
@ -182,7 +182,7 @@ class ConversationRenameApi(Resource):
|
|||||||
def post(self, app_model: App, end_user: EndUser, c_id):
|
def post(self, app_model: App, end_user: EndUser, c_id):
|
||||||
"""Rename a conversation or auto-generate a name."""
|
"""Rename a conversation or auto-generate a name."""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
conversation_id = str(c_id)
|
conversation_id = str(c_id)
|
||||||
@ -224,7 +224,7 @@ class ConversationVariablesApi(Resource):
|
|||||||
"""
|
"""
|
||||||
# conversational variable only for chat app
|
# conversational variable only for chat app
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
conversation_id = str(c_id)
|
conversation_id = str(c_id)
|
||||||
@ -263,7 +263,7 @@ class ConversationVariableDetailApi(Resource):
|
|||||||
The value must match the variable's expected type.
|
The value must match the variable's expected type.
|
||||||
"""
|
"""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
conversation_id = str(c_id)
|
conversation_id = str(c_id)
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class MessageListApi(Resource):
|
|||||||
Retrieves messages with pagination support using first_id.
|
Retrieves messages with pagination support using first_id.
|
||||||
"""
|
"""
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
query_args = MessageListQuery.model_validate(request.args.to_dict())
|
query_args = MessageListQuery.model_validate(request.args.to_dict())
|
||||||
@ -170,7 +170,7 @@ class MessageSuggestedApi(Resource):
|
|||||||
"""
|
"""
|
||||||
message_id = str(message_id)
|
message_id = str(message_id)
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||||
raise NotChatAppError()
|
raise NotChatAppError()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -87,6 +87,37 @@ class AgentV2Node(Node[AgentV2NodeData]):
|
|||||||
def version(cls) -> str:
|
def version(cls) -> str:
|
||||||
return "1"
|
return "1"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_config(cls, filters: Mapping[str, object] | None = None) -> Mapping[str, object]:
|
||||||
|
return {
|
||||||
|
"type": AGENT_V2_NODE_TYPE,
|
||||||
|
"config": {
|
||||||
|
"prompt_templates": {
|
||||||
|
"chat_model": {
|
||||||
|
"prompts": [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"text": "You are a helpful AI assistant.",
|
||||||
|
"edition_type": "basic",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"completion_model": {
|
||||||
|
"conversation_histories_role": {
|
||||||
|
"user_prefix": "Human",
|
||||||
|
"assistant_prefix": "Assistant",
|
||||||
|
},
|
||||||
|
"prompt": {
|
||||||
|
"text": "{{#sys.query#}}",
|
||||||
|
"edition_type": "basic",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"agent_strategy": "auto",
|
||||||
|
"max_iterations": 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def _run(self) -> Generator[NodeEventBase, None, None]:
|
def _run(self) -> Generator[NodeEventBase, None, None]:
|
||||||
dify_ctx = DifyRunContext.model_validate(self.require_run_context_value(DIFY_RUN_CONTEXT_KEY))
|
dify_ctx = DifyRunContext.model_validate(self.require_run_context_value(DIFY_RUN_CONTEXT_KEY))
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,115 @@
|
|||||||
|
"""Tests for Phase 7 — New/old agent node parallel compatibility."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
class TestAgentV2DefaultConfig:
|
||||||
|
"""Verify Agent V2 node provides default block configuration."""
|
||||||
|
|
||||||
|
def test_has_default_config(self):
|
||||||
|
from core.workflow.node_factory import register_nodes
|
||||||
|
|
||||||
|
register_nodes()
|
||||||
|
|
||||||
|
from graphon.nodes.base.node import Node
|
||||||
|
|
||||||
|
registry = Node.get_node_type_classes_mapping()
|
||||||
|
agent_v2_cls = registry["agent-v2"]["latest"]
|
||||||
|
config = agent_v2_cls.get_default_config()
|
||||||
|
|
||||||
|
assert config, "Agent V2 should have a default config"
|
||||||
|
assert config["type"] == "agent-v2"
|
||||||
|
assert "config" in config
|
||||||
|
assert "prompt_templates" in config["config"]
|
||||||
|
assert "agent_strategy" in config["config"]
|
||||||
|
assert config["config"]["agent_strategy"] == "auto"
|
||||||
|
assert config["config"]["max_iterations"] == 10
|
||||||
|
|
||||||
|
def test_old_agent_no_default_config(self):
|
||||||
|
from core.workflow.node_factory import register_nodes
|
||||||
|
|
||||||
|
register_nodes()
|
||||||
|
|
||||||
|
from graphon.nodes.base.node import Node
|
||||||
|
|
||||||
|
registry = Node.get_node_type_classes_mapping()
|
||||||
|
agent_cls = registry["agent"]["latest"]
|
||||||
|
config = agent_cls.get_default_config()
|
||||||
|
assert config == {} or config is None or not config
|
||||||
|
|
||||||
|
|
||||||
|
class TestParallelNodeRegistration:
|
||||||
|
"""Verify both agent and agent-v2 coexist in the registry."""
|
||||||
|
|
||||||
|
def test_both_registered(self):
|
||||||
|
from core.workflow.node_factory import register_nodes
|
||||||
|
|
||||||
|
register_nodes()
|
||||||
|
|
||||||
|
from graphon.nodes.base.node import Node
|
||||||
|
|
||||||
|
registry = Node.get_node_type_classes_mapping()
|
||||||
|
assert "agent" in registry
|
||||||
|
assert "agent-v2" in registry
|
||||||
|
|
||||||
|
def test_different_classes(self):
|
||||||
|
from core.workflow.node_factory import register_nodes
|
||||||
|
|
||||||
|
register_nodes()
|
||||||
|
|
||||||
|
from graphon.nodes.base.node import Node
|
||||||
|
|
||||||
|
registry = Node.get_node_type_classes_mapping()
|
||||||
|
old_cls = registry["agent"]["latest"]
|
||||||
|
new_cls = registry["agent-v2"]["latest"]
|
||||||
|
assert old_cls is not new_cls
|
||||||
|
|
||||||
|
def test_default_configs_list_contains_agent_v2(self):
|
||||||
|
"""Verify agent-v2 appears in the full default block configs list.
|
||||||
|
|
||||||
|
Instead of instantiating WorkflowService (which requires Flask/DB),
|
||||||
|
we replicate the same iteration logic over the node registry.
|
||||||
|
"""
|
||||||
|
from core.workflow.node_factory import LATEST_VERSION, get_node_type_classes_mapping, register_nodes
|
||||||
|
|
||||||
|
register_nodes()
|
||||||
|
|
||||||
|
types_with_config: set[str] = set()
|
||||||
|
for node_type, mapping in get_node_type_classes_mapping().items():
|
||||||
|
node_cls = mapping.get(LATEST_VERSION)
|
||||||
|
if node_cls:
|
||||||
|
cfg = node_cls.get_default_config()
|
||||||
|
if cfg and isinstance(cfg, dict):
|
||||||
|
types_with_config.add(cfg.get("type", ""))
|
||||||
|
|
||||||
|
assert "agent-v2" in types_with_config
|
||||||
|
|
||||||
|
|
||||||
|
class TestAgentModeWorkflowAccess:
|
||||||
|
"""Verify AGENT mode is allowed in workflow-related API mode checks."""
|
||||||
|
|
||||||
|
def test_workflow_controller_allows_agent(self):
|
||||||
|
"""Check that the workflow.py source allows AppMode.AGENT."""
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from controllers.console.app import workflow
|
||||||
|
|
||||||
|
source = inspect.getsource(workflow)
|
||||||
|
assert "AppMode.AGENT" in source
|
||||||
|
|
||||||
|
def test_service_api_chat_allows_agent(self):
|
||||||
|
"""Check that service API chat endpoint allows AGENT mode."""
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from controllers.service_api.app import completion
|
||||||
|
|
||||||
|
source = inspect.getsource(completion)
|
||||||
|
assert "AppMode.AGENT" in source
|
||||||
|
|
||||||
|
def test_service_api_conversation_allows_agent(self):
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from controllers.service_api.app import conversation
|
||||||
|
|
||||||
|
source = inspect.getsource(conversation)
|
||||||
|
assert "AppMode.AGENT" in source
|
||||||
Loading…
Reference in New Issue
Block a user