From 731524ce9d054b89704bc3e01878c7e5e9510e13 Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Thu, 8 Oct 2026 18:34:53 +0800 Subject: [PATCH 1/8] refactor(api): serve Agent logs through an application service --- api/controllers/console/app/agent.py | 41 +-- api/extensions/application_services/agent.py | 13 + api/services/agent/log_file_gateway.py | 33 ++ api/services/agent/log_service.py | 96 +++++ .../console/app/test_agent_logs.py | 91 +++++ .../services/agent/test_log_service.py | 335 ++++++++++++++++++ 6 files changed, 589 insertions(+), 20 deletions(-) create mode 100644 api/services/agent/log_file_gateway.py create mode 100644 api/services/agent/log_service.py create mode 100644 api/tests/unit_tests/controllers/console/app/test_agent_logs.py create mode 100644 api/tests/unit_tests/services/agent/test_log_service.py diff --git a/api/controllers/console/app/agent.py b/api/controllers/console/app/agent.py index 619d7da6371c3e..33571658f14395 100644 --- a/api/controllers/console/app/agent.py +++ b/api/controllers/console/app/agent.py @@ -1,26 +1,23 @@ from typing import Any +from uuid import UUID from flask_restx import Resource from pydantic import BaseModel, Field, field_validator -from sqlalchemy.orm import Session from controllers.common.rbac import PlainApp, RBACCheck from controllers.common.schema import query_params_from_model, register_response_schema_models -from controllers.common.session import with_session from controllers.console import console_ns -from controllers.console.app.wraps import get_app_model +from controllers.console.app.error import AppNotFoundError +from controllers.console.flask_admission import console_account_admission from controllers.console.wraps import ( RBACPermission, - account_initialization_required, model_validate, - rbac_permission_required, - setup_required, ) +from extensions.ext_application_services import application_services from fields.base import ResponseModel -from libs.helper import uuid_value -from libs.login import login_required -from models.model import App, AppMode -from services.agent_service import AgentService +from libs.helper import dump_response, uuid_value +from machinery.context import RequestContext +from services.agent.log_contracts import AgentLogAppNotFoundError class AgentLogQuery(BaseModel): @@ -49,8 +46,8 @@ class AgentToolCallResponse(ResponseModel): time_cost: float | int tool_name: str tool_label: str - tool_input: dict[str, Any] - tool_output: dict[str, Any] + tool_input: Any + tool_output: Any tool_parameters: dict[str, Any] tool_icon: Any = Field(default=None) @@ -81,14 +78,18 @@ class AgentLogApi(Resource): @console_ns.doc(params=query_params_from_model(AgentLogQuery)) @console_ns.response(200, "Agent logs retrieved successfully", console_ns.models[AgentLogResponse.__name__]) @console_ns.response(400, "Invalid request parameters") - @setup_required - @login_required - @account_initialization_required - @rbac_permission_required(RBACCheck(RBACPermission.APP_VIEW_LAYOUT, PlainApp())) - @with_session(write=False) - @get_app_model(mode=[AppMode.AGENT_CHAT]) + @console_account_admission(rbac_checks=[RBACCheck(RBACPermission.APP_VIEW_LAYOUT, PlainApp())]) @model_validate(AgentLogQuery) - def get(self, req_data: AgentLogQuery, session: Session, app_model: App): + def get(self, req_data: AgentLogQuery, request_context: RequestContext, app_id: UUID): """Get agent logs.""" - return AgentService.get_agent_logs(app_model, req_data.conversation_id, req_data.message_id, session) + try: + result = application_services().agent_apps.logs.get( + request_context, + app_id=str(app_id), + conversation_id=req_data.conversation_id, + message_id=req_data.message_id, + ) + except AgentLogAppNotFoundError as error: + raise AppNotFoundError() from error + return dump_response(AgentLogResponse, result) diff --git a/api/extensions/application_services/agent.py b/api/extensions/application_services/agent.py index d7aa6b0f9b8f22..d88ddff4b3a8fc 100644 --- a/api/extensions/application_services/agent.py +++ b/api/extensions/application_services/agent.py @@ -1,21 +1,29 @@ """Composition of the Agent App console use cases.""" from dataclasses import dataclass +from functools import partial from sqlalchemy.orm import Session, sessionmaker from configs import dify_config +from core.app.file_access import DatabaseFileAccessController from repositories.app.agent_app_repository import AgentAppRepository from repositories.app.agent_sandbox_repository import AgentSandboxRepository +from repositories.app.generation_repository import AppGenerationRepository +from repositories.tools.provider_repository import ToolProviderRepository +from services.agent.log_file_gateway import AgentLogFileGateway +from services.agent.log_service import AgentLogService from services.app.agent_app_feature_gateway import AgentAppFeatureValidator from services.app.agent_app_sandbox_service import AgentAppSandboxService from services.app.agent_app_service import AgentAppAccessService, AgentAppFeatureConfigService from services.app.agent_sandbox_file_gateway import AgentSandboxFileGateway, create_sandbox_client from services.file_request_service import FileRequestService +from services.tools.tool_manager import ToolManager @dataclass(frozen=True, slots=True) class AgentAppServices: + logs: AgentLogService access: AgentAppAccessService features: AgentAppFeatureConfigService sandbox: AgentAppSandboxService @@ -24,6 +32,11 @@ class AgentAppServices: def build_agent_app_services(*, database_client: sessionmaker[Session]) -> AgentAppServices: apps = AgentAppRepository(session_factory=database_client) return AgentAppServices( + logs=AgentLogService( + records=AppGenerationRepository(database_client), + icons=partial(ToolManager.get_tool_icon, tool_providers=ToolProviderRepository(database_client)), + files=AgentLogFileGateway(sessions=database_client, access=DatabaseFileAccessController()), + ), access=AgentAppAccessService(references=apps), features=AgentAppFeatureConfigService(features=apps, validator=AgentAppFeatureValidator()), sandbox=AgentAppSandboxService( diff --git a/api/services/agent/log_file_gateway.py b/api/services/agent/log_file_gateway.py new file mode 100644 index 00000000000000..399c7ce527aef2 --- /dev/null +++ b/api/services/agent/log_file_gateway.py @@ -0,0 +1,33 @@ +"""Rebuild message attachments with the shared file factory and explicit sessions.""" + +from typing import Any + +from sqlalchemy.orm import Session, sessionmaker + +from core.app.file_access import FileAccessControllerProtocol +from factories.file_factory import build_from_mapping +from graphon.file import FileTransferMethod + + +class AgentLogFileGateway: + def __init__(self, *, sessions: sessionmaker[Session], access: FileAccessControllerProtocol) -> None: + self._sessions = sessions + self._access = access + + def resolve(self, *, tenant_id: str, files: list[dict[str, Any]]) -> list[dict[str, Any]]: + result = [] + for source in files: + mapping = dict(source) + if mapping["transfer_method"] == FileTransferMethod.TOOL_FILE: + reference = mapping["upload_file_id"] + if reference is None: + reference = mapping["url"].split("/")[-1].split(".")[0] + mapping["tool_file_id"] = reference + mapping["upload_file_id"] = reference + file = build_from_mapping( + mapping=mapping, tenant_id=tenant_id, access_controller=self._access, sessions=self._sessions + ) + result.append( + {"belongs_to": source["belongs_to"], "upload_file_id": mapping["upload_file_id"], **file.to_dict()} + ) + return result diff --git a/api/services/agent/log_service.py b/api/services/agent/log_service.py new file mode 100644 index 00000000000000..a3dcf663fd477c --- /dev/null +++ b/api/services/agent/log_service.py @@ -0,0 +1,96 @@ +"""Present one Agent message log after its read transaction has closed.""" + +from datetime import UTC +from typing import Any + +import pytz + +from core.app.app_config.easy_ui_based_app.agent.manager import AgentConfigManager +from core.tools.entities.tool_entities import EmojiIconDict +from libs.datetime_utils import ensure_naive_utc +from machinery.context import RequestContext +from services.agent.log_contracts import AgentLogConfigurationError, AgentLogFiles, AgentLogIcons, AgentLogRecords + + +class AgentLogService: + def __init__(self, *, records: AgentLogRecords, icons: AgentLogIcons, files: AgentLogFiles) -> None: + self._records = records + self._icons = icons + self._files = files + + def get(self, context: RequestContext, *, app_id: str, conversation_id: str, message_id: str) -> dict[str, Any]: + log = self._records.agent_log( + tenant_id=context.active_workspace_id, + account_id=context.account_id, + app_id=app_id, + conversation_id=conversation_id, + message_id=message_id, + ) + agent_config = AgentConfigManager.convert(log.model_config) + if agent_config is None: + raise AgentLogConfigurationError("Agent config not found") + configured_tools = {tool.tool_name: tool for tool in agent_config.tools or []} + iterations = [] + for thought in log.thoughts: + tool_calls = [] + for name in thought.tools: + metadata = thought.metadata.get(name, {}) + config = metadata.get("tool_config", {}) + provider_type = config.get("tool_provider_type", "") + provider_id = config.get("tool_provider", "") + configured = configured_tools.get(name) + if not provider_type and configured is not None: + provider_type = configured.provider_type + provider_id = provider_id or configured.provider_id + icon: str | EmojiIconDict = "" + if provider_type and provider_type != "dataset-retrieval": + icon = self._icons( + tenant_id=context.active_workspace_id, + provider_type=provider_type, + provider_id=provider_id, + ) + if not icon and configured is not None: + icon = self._icons( + tenant_id=context.active_workspace_id, + provider_type=configured.provider_type, + provider_id=configured.provider_id, + ) + tool_calls.append( + { + "status": "success" if not metadata.get("error") else "error", + "error": metadata.get("error"), + "time_cost": metadata.get("time_cost", 0), + "tool_name": name, + "tool_label": thought.labels.get(name, name), + "tool_input": thought.inputs.get(name, {}), + "tool_output": thought.outputs.get(name, {}), + "tool_parameters": metadata.get("tool_parameters", {}), + "tool_icon": icon, + } + ) + iterations.append( + { + "tokens": thought.tokens or 0, + "tool_calls": tool_calls, + "tool_raw": {"inputs": thought.raw_input, "outputs": thought.raw_output}, + "thought": thought.thought, + "created_at": thought.created_at.isoformat(), + "files": thought.files, + } + ) + return { + "meta": { + "status": "success", + "executor": log.executor, + "start_time": ensure_naive_utc(log.created_at) + .replace(tzinfo=UTC) + .astimezone(pytz.timezone(log.timezone)) + .isoformat(), + "elapsed_time": log.elapsed_time, + "total_tokens": log.total_tokens, + "agent_mode": log.model_config["agent_mode"].get("strategy", "react"), + "iterations": len(iterations), + }, + "iterations": iterations, + "files": self._files.resolve(tenant_id=context.active_workspace_id, files=log.files), + } diff --git a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py new file mode 100644 index 00000000000000..239d7e1c387ae9 --- /dev/null +++ b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py @@ -0,0 +1,91 @@ +"""Agent log transport delegates stable identity without acquiring a Session.""" + +from dataclasses import dataclass, field +from inspect import unwrap +from uuid import UUID + +import pytest + +from controllers.console.app import agent +from controllers.console.app.error import AppNotFoundError +from controllers.console.wraps import RBACPermission +from machinery.context import RequestContext +from services.agent.log_contracts import AgentLogAppNotFoundError +from tests.unit_tests.controllers.rbac_introspection import rbac_checks + +CONTEXT = RequestContext("request", "trace", "viewer", "tenant") +APP_ID = UUID("00000000-0000-0000-0000-000000000001") +MESSAGE_ID = "00000000-0000-0000-0000-000000000002" +CONVERSATION_ID = "00000000-0000-0000-0000-000000000003" + + +@dataclass +class LogService: + error: Exception | None = None + calls: list = field(default_factory=list) + + def get(self, context, **kwargs): + self.calls.append((context, kwargs)) + if self.error: + raise self.error + return { + "meta": { + "status": "success", + "executor": "user", + "start_time": "2024-01-01T00:00:00+00:00", + "total_tokens": 3, + "agent_mode": "react", + "iterations": 1, + }, + "iterations": [ + { + "tokens": 3, + "created_at": "2024-01-01T00:00:00", + "thought": "Read", + "tool_raw": {}, + "tool_calls": [ + { + "status": "success", + "time_cost": 0, + "tool_name": "search", + "tool_label": "Search", + "tool_input": {}, + "tool_output": "text output", + "tool_parameters": {}, + } + ], + } + ], + "files": [], + } + + +@dataclass +class AgentServices: + logs: LogService + + +@dataclass +class Services: + agent_apps: AgentServices + + +@pytest.mark.parametrize("missing", [False, True]) +def test_log_controller_delegates_context_and_serializes(monkeypatch, missing): + logs = LogService(AgentLogAppNotFoundError() if missing else None) + monkeypatch.setattr(agent, "application_services", lambda: Services(AgentServices(logs))) + method = unwrap(agent.AgentLogApi.get) + payload = agent.AgentLogQuery(message_id=MESSAGE_ID, conversation_id=CONVERSATION_ID) + if missing: + with pytest.raises(AppNotFoundError): + method(object(), payload, CONTEXT, APP_ID) + else: + response = method(object(), payload, CONTEXT, APP_ID) + assert response["iterations"][0]["tool_calls"][0]["tool_output"] == "text output" + assert logs.calls == [ + (CONTEXT, {"app_id": str(APP_ID), "message_id": MESSAGE_ID, "conversation_id": CONVERSATION_ID}) + ] + + +def test_log_admission_preserves_view_layout_permission(): + assert [check.scene for check in rbac_checks(agent.AgentLogApi.get)] == [RBACPermission.APP_VIEW_LAYOUT] diff --git a/api/tests/unit_tests/services/agent/test_log_service.py b/api/tests/unit_tests/services/agent/test_log_service.py new file mode 100644 index 00000000000000..993310df381239 --- /dev/null +++ b/api/tests/unit_tests/services/agent/test_log_service.py @@ -0,0 +1,335 @@ +"""Agent log retrieval through production composition and an isolated database.""" + +import json +from datetime import datetime +from decimal import Decimal +from uuid import uuid4 + +import pytest +from flask import has_app_context +from sqlalchemy import create_engine, select +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import QueuePool + +from core.db import session_factory +from extensions.application_services.agent import build_agent_app_services +from graphon.file import FileTransferMethod +from machinery.context import RequestContext +from models.account import Account +from models.base import TypeBase +from models.enums import ConversationFromSource, CreatorUserRole, EndUserType, MessageFileBelongsTo +from models.model import ( + App, + AppAnnotationSetting, + AppMode, + AppModelConfig, + Conversation, + EndUser, + Message, + MessageAgentThought, + MessageFile, + UploadFile, +) +from models.tools import ApiToolProvider, ToolFile +from services.agent.log_contracts import AgentLogAppNotFoundError, AgentLogConfigurationError, AgentLogNotFoundError +from tests.unit_tests.model_factories import make_account, make_app, make_conversation, make_message, make_upload_file + +CONTEXT = RequestContext("request", None, "viewer", "tenant") + + +@pytest.fixture(autouse=True) +def _provide_app_context(): + """This application use case must work without Flask or current_user.""" + + +@pytest.fixture +def store(monkeypatch): + engine = create_engine("sqlite://", poolclass=QueuePool) + TypeBase.metadata.create_all( + engine, + tables=[ + m.__table__ + for m in ( + App, + Account, + Conversation, + Message, + AppModelConfig, + MessageAgentThought, + MessageFile, + UploadFile, + ToolFile, + ApiToolProvider, + AppAnnotationSetting, + EndUser, + ) + ], + ) + sessions = sessionmaker(engine, expire_on_commit=False) + + def reject_global(*_args, **_kwargs): + pytest.fail("Agent logs accessed a global database or user") + + monkeypatch.setattr(session_factory, "create_session", reject_global) + from extensions.ext_database import db + + monkeypatch.setattr(db, "session", reject_global) + monkeypatch.setattr(type(db), "engine", property(reject_global)) + with sessions.begin() as session: + account = make_account(account_id="viewer", timezone="Asia/Shanghai", name="Viewer") + config = AppModelConfig( + app_id="app", + model=json.dumps({"provider": "openai", "name": "gpt", "mode": "chat"}), + agent_mode=json.dumps( + { + "enabled": True, + "strategy": "react", + "tools": [ + {"enabled": True, "provider_type": "api", "provider_id": "provider", "tool_name": "search"} + ], + } + ), + ) + session.add_all([account, config]) + session.flush() + session.add(make_app(app_id="app", tenant_id="tenant", mode=AppMode.AGENT_CHAT, app_model_config_id=config.id)) + session.add( + make_conversation( + conversation_id="conversation", + app_id="app", + from_account_id="viewer", + inputs={}, + from_source=ConversationFromSource.CONSOLE, + ) + ) + session.add( + make_message( + message_id="message", + app_id="app", + conversation_id="conversation", + inputs={}, + query="Hi", + message={}, + answer="Hello", + message_unit_price=Decimal(0), + answer_unit_price=Decimal(0), + currency="USD", + from_source=ConversationFromSource.CONSOLE, + from_account_id="viewer", + message_tokens=7, + answer_tokens=3, + created_at=datetime(2024, 1, 1), + provider_response_latency=1.5, + ) + ) + provider = ApiToolProvider( + tenant_id="tenant", + user_id="viewer", + name="api", + description="Search", + schema="{}", + schema_type_str="openapi", + icon='{"content":"S","background":"#000"}', + tools_str="[]", + credentials_str="{}", + ) + provider.id = "provider" + session.add(provider) + session.add( + MessageAgentThought( + message_id="message", + position=1, + created_by_role=CreatorUserRole.ACCOUNT, + created_by="viewer", + thought="Search first", + tool="search", + tool_labels_str='{"search":"Search"}', + tokens=10, + tool_meta_str='{"search":{"tool_parameters":{"limit":3},"time_cost":1.2}}', + tool_input='{"query":"test"}', + observation='{"result":"found"}', + message_files='["generated-file"]', + ) + ) + service = build_agent_app_services(database_client=sessions).logs + try: + yield sessions, service + finally: + engine.dispose() + + +def get_log(service, context=CONTEXT, **kwargs): + return service.get( + context, **{"app_id": "app", "conversation_id": "conversation", "message_id": "message", **kwargs} + ) + + +def test_log_uses_loaded_timezone_configuration_and_tool_metadata(store): + assert not has_app_context() + sessions, service = store + result = get_log(service) + assert result["meta"] == { + "status": "success", + "executor": "Viewer", + "start_time": "2024-01-01T08:00:00+08:00", + "elapsed_time": 1.5, + "total_tokens": 10, + "agent_mode": "react", + "iterations": 1, + } + thought = result["iterations"][0] + assert thought["tokens"] == 10 + assert thought["files"] == ["generated-file"] + assert thought["tool_calls"] == [ + { + "status": "success", + "error": None, + "time_cost": 1.2, + "tool_name": "search", + "tool_label": "Search", + "tool_input": {"query": "test"}, + "tool_output": {"result": "found"}, + "tool_parameters": {"limit": 3}, + "tool_icon": {"content": "S", "background": "#000"}, + } + ] + assert sessions.kw["bind"].pool.checkedout() == 0 + + +@pytest.mark.parametrize("failure", ["workspace", "mode", "conversation", "message", "config"]) +def test_log_enforces_complete_owner_chain(store, failure): + sessions, service = store + context = CONTEXT + with sessions.begin() as session: + if failure == "workspace": + context = CONTEXT._replace(active_workspace_id="foreign") + elif failure == "mode": + session.get(App, "app").mode = AppMode.WORKFLOW + elif failure == "conversation": + session.get(Conversation, "conversation").app_id = "another-app" + elif failure == "message": + session.get(Message, "message").app_id = "another-app" + else: + session.get(App, "app").app_model_config_id = None + error = ( + AgentLogAppNotFoundError + if failure in {"workspace", "mode"} + else (AgentLogConfigurationError if failure == "config" else AgentLogNotFoundError) + ) + with pytest.raises(error): + get_log(service, context) + + +@pytest.mark.parametrize( + "file_type", [FileTransferMethod.LOCAL_FILE, FileTransferMethod.REMOTE_URL, FileTransferMethod.TOOL_FILE] +) +def test_log_releases_read_transaction_before_file_processing(store, monkeypatch, file_type): + sessions, service = store + remote_calls = [] + + def remote_info(url): + assert sessions.kw["bind"].pool.checkedout() == 0 + remote_calls.append(url) + return "text/plain", "remote.txt", 42 + + monkeypatch.setattr("factories.file_factory.builders.get_remote_file_info", remote_info) + with sessions.begin() as session: + reference = str(uuid4()) + if file_type == FileTransferMethod.LOCAL_FILE: + session.add(make_upload_file(file_id=reference, tenant_id="tenant", name="local.txt")) + elif file_type == FileTransferMethod.TOOL_FILE: + tool_file = ToolFile( + user_id="viewer", + tenant_id="tenant", + conversation_id="conversation", + file_key="generated/result.txt", + mimetype="text/plain", + name="result.txt", + size=15, + ) + tool_file.id = reference + session.add(tool_file) + session.add( + MessageFile( + message_id="message", + type="document", + transfer_method=file_type, + url="https://example.com/remote.txt" + if file_type == FileTransferMethod.REMOTE_URL + else f"/files/tools/{reference}.txt", + upload_file_id=reference if file_type == FileTransferMethod.LOCAL_FILE else None, + belongs_to=MessageFileBelongsTo.USER, + created_by_role=CreatorUserRole.ACCOUNT, + created_by="viewer", + ) + ) + result = get_log(service) + assert ( + result["files"][0]["filename"] + == { + FileTransferMethod.LOCAL_FILE: "local.txt", + FileTransferMethod.REMOTE_URL: "remote.txt", + FileTransferMethod.TOOL_FILE: "result.txt", + }[file_type] + ) + assert result["files"][0]["belongs_to"] == "user" + assert len(remote_calls) == (1 if file_type == FileTransferMethod.REMOTE_URL else 0) + assert sessions.kw["bind"].pool.checkedout() == 0 + with sessions() as session: + file = session.scalar(select(MessageFile)) + if file_type == FileTransferMethod.TOOL_FILE: + assert file.upload_file_id is None # Viewing a log does not rewrite attachment records. + + +def test_log_handles_missing_executor_and_malformed_thought_metadata(store): + sessions, service = store + with sessions.begin() as session: + session.get(Conversation, "conversation").from_account_id = "deleted" + thought = session.scalar(select(MessageAgentThought)) + thought.tool_labels_str = thought.tool_meta_str = thought.tool_input = thought.observation = "invalid-json" + result = get_log(service) + assert result["meta"]["executor"] == "Unknown" + tool = result["iterations"][0]["tool_calls"][0] + assert tool["tool_label"] == "search" + assert tool["tool_input"] == {} + assert tool["tool_output"] == "invalid-json" + + +@pytest.mark.parametrize("tenant_id", ["tenant", "foreign"]) +def test_log_resolves_end_user_inside_workspace(store, tenant_id): + sessions, service = store + with sessions.begin() as session: + user = EndUser( + tenant_id=tenant_id, app_id="app", name="Customer", type=EndUserType.SERVICE_API, session_id="session" + ) + session.add(user) + session.flush() + conversation = session.get(Conversation, "conversation") + conversation.from_account_id = None + conversation.from_end_user_id = user.id + assert get_log(service)["meta"]["executor"] == ("Customer" if tenant_id == "tenant" else "Unknown") + + +def test_log_preserves_tool_errors_and_empty_iterations(store): + sessions, service = store + with sessions.begin() as session: + thought = session.scalar(select(MessageAgentThought)) + thought.tool_meta_str = '{"search":{"error":"Remote unavailable","time_cost":2}}' + thought.tokens = None + result = get_log(service) + assert result["iterations"][0]["tokens"] == 0 + tool = result["iterations"][0]["tool_calls"][0] + assert (tool["status"], tool["error"], tool["time_cost"]) == ("error", "Remote unavailable", 2) + with sessions.begin() as session: + session.delete(session.scalar(select(MessageAgentThought))) + result = get_log(service) + assert result["meta"]["iterations"] == 0 + assert result["iterations"] == [] + + +def test_log_rejects_disabled_agent_configuration(store): + sessions, service = store + with sessions.begin() as session: + session.scalar(select(AppModelConfig)).agent_mode = '{"enabled":false}' + with pytest.raises(AgentLogConfigurationError, match="Agent config not found"): + get_log(service) From 8fea94fad50a78e42837de1cb6469c7a398f26c3 Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Fri, 9 Oct 2026 12:39:28 +0800 Subject: [PATCH 2/8] test(agent): type log transport and database-backed presentation contracts --- .../console/app/test_agent_logs.py | 51 ++++-- .../services/agent/test_log_service.py | 155 +++++++++++++----- 2 files changed, 150 insertions(+), 56 deletions(-) diff --git a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py index 239d7e1c387ae9..502dbc7ae81e52 100644 --- a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py +++ b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py @@ -1,5 +1,6 @@ """Agent log transport delegates stable identity without acquiring a Session.""" +from collections.abc import Mapping from dataclasses import dataclass, field from inspect import unwrap from uuid import UUID @@ -18,14 +19,33 @@ MESSAGE_ID = "00000000-0000-0000-0000-000000000002" CONVERSATION_ID = "00000000-0000-0000-0000-000000000003" +type LogCall = tuple[RequestContext, dict[str, str]] + + +def response_mapping(value: object) -> dict[str, object]: + assert isinstance(value, Mapping) + return {key: item for key, item in value.items() if isinstance(key, str)} + @dataclass class LogService: error: Exception | None = None - calls: list = field(default_factory=list) + calls: list[LogCall] = field(default_factory=list) - def get(self, context, **kwargs): - self.calls.append((context, kwargs)) + def get( + self, + context: RequestContext, + *, + app_id: str, + conversation_id: str, + message_id: str, + ) -> dict[str, object]: + self.calls.append( + ( + context, + {"app_id": app_id, "conversation_id": conversation_id, "message_id": message_id}, + ) + ) if self.error: raise self.error return { @@ -42,21 +62,21 @@ def get(self, context, **kwargs): "tokens": 3, "created_at": "2024-01-01T00:00:00", "thought": "Read", - "tool_raw": {}, + "tool_raw": dict[str, object](), "tool_calls": [ { "status": "success", "time_cost": 0, "tool_name": "search", "tool_label": "Search", - "tool_input": {}, + "tool_input": dict[str, object](), "tool_output": "text output", - "tool_parameters": {}, + "tool_parameters": dict[str, object](), } ], } ], - "files": [], + "files": list[object](), } @@ -71,21 +91,26 @@ class Services: @pytest.mark.parametrize("missing", [False, True]) -def test_log_controller_delegates_context_and_serializes(monkeypatch, missing): +def test_log_controller_delegates_context_and_serializes(monkeypatch: pytest.MonkeyPatch, missing: bool) -> None: logs = LogService(AgentLogAppNotFoundError() if missing else None) monkeypatch.setattr(agent, "application_services", lambda: Services(AgentServices(logs))) - method = unwrap(agent.AgentLogApi.get) + endpoint = agent.AgentLogApi() + method = unwrap(endpoint.get) payload = agent.AgentLogQuery(message_id=MESSAGE_ID, conversation_id=CONVERSATION_ID) if missing: with pytest.raises(AppNotFoundError): - method(object(), payload, CONTEXT, APP_ID) + method(endpoint, payload, CONTEXT, APP_ID) else: - response = method(object(), payload, CONTEXT, APP_ID) - assert response["iterations"][0]["tool_calls"][0]["tool_output"] == "text output" + response = response_mapping(method(endpoint, payload, CONTEXT, APP_ID)) + iterations = response["iterations"] + assert isinstance(iterations, list) + tool_calls = response_mapping(iterations[0])["tool_calls"] + assert isinstance(tool_calls, list) + assert response_mapping(tool_calls[0])["tool_output"] == "text output" assert logs.calls == [ (CONTEXT, {"app_id": str(APP_ID), "message_id": MESSAGE_ID, "conversation_id": CONVERSATION_ID}) ] -def test_log_admission_preserves_view_layout_permission(): +def test_log_admission_preserves_view_layout_permission() -> None: assert [check.scene for check in rbac_checks(agent.AgentLogApi.get)] == [RBACPermission.APP_VIEW_LAYOUT] diff --git a/api/tests/unit_tests/services/agent/test_log_service.py b/api/tests/unit_tests/services/agent/test_log_service.py index 993310df381239..baea8ae1c75c0a 100644 --- a/api/tests/unit_tests/services/agent/test_log_service.py +++ b/api/tests/unit_tests/services/agent/test_log_service.py @@ -1,19 +1,22 @@ """Agent log retrieval through production composition and an isolated database.""" import json +from collections.abc import Iterator from datetime import datetime from decimal import Decimal +from typing import Literal, TypedDict from uuid import uuid4 import pytest from flask import has_app_context -from sqlalchemy import create_engine, select -from sqlalchemy.orm import sessionmaker +from sqlalchemy import Table, create_engine, select +from sqlalchemy.orm import Session, sessionmaker from sqlalchemy.pool import QueuePool from core.db import session_factory +from core.tools.entities.tool_entities import ApiProviderSchemaType from extensions.application_services.agent import build_agent_app_services -from graphon.file import FileTransferMethod +from graphon.file import FileTransferMethod, FileType from machinery.context import RequestContext from models.account import Account from models.base import TypeBase @@ -32,42 +35,84 @@ ) from models.tools import ApiToolProvider, ToolFile from services.agent.log_contracts import AgentLogAppNotFoundError, AgentLogConfigurationError, AgentLogNotFoundError +from services.agent.log_service import AgentLogService from tests.unit_tests.model_factories import make_account, make_app, make_conversation, make_message, make_upload_file CONTEXT = RequestContext("request", None, "viewer", "tenant") +type AgentLogStore = tuple[sessionmaker[Session], AgentLogService] + + +class AgentToolCall(TypedDict): + status: str + error: object | None + time_cost: float | int + tool_name: str + tool_label: str + tool_input: object + tool_output: object + tool_parameters: dict[str, object] + tool_icon: object + + +class AgentIteration(TypedDict): + tokens: int + tool_calls: list[AgentToolCall] + tool_raw: dict[str, object] + thought: str | None + created_at: str + files: list[object] + + +class AgentLogMeta(TypedDict): + status: str + executor: str + start_time: str + elapsed_time: float + total_tokens: int + agent_mode: str + iterations: int + + +class AgentLogResult(TypedDict): + meta: AgentLogMeta + iterations: list[AgentIteration] + files: list[dict[str, object]] + @pytest.fixture(autouse=True) -def _provide_app_context(): +def _provide_app_context() -> None: """This application use case must work without Flask or current_user.""" @pytest.fixture -def store(monkeypatch): +def store(monkeypatch: pytest.MonkeyPatch) -> Iterator[AgentLogStore]: engine = create_engine("sqlite://", poolclass=QueuePool) + tables: list[Table] = [] + for model in ( + App, + Account, + Conversation, + Message, + AppModelConfig, + MessageAgentThought, + MessageFile, + UploadFile, + ToolFile, + ApiToolProvider, + AppAnnotationSetting, + EndUser, + ): + table = model.__table__ + assert isinstance(table, Table) + tables.append(table) TypeBase.metadata.create_all( engine, - tables=[ - m.__table__ - for m in ( - App, - Account, - Conversation, - Message, - AppModelConfig, - MessageAgentThought, - MessageFile, - UploadFile, - ToolFile, - ApiToolProvider, - AppAnnotationSetting, - EndUser, - ) - ], + tables=tables, ) sessions = sessionmaker(engine, expire_on_commit=False) - def reject_global(*_args, **_kwargs): + def reject_global(*_args: object, **_kwargs: object) -> None: pytest.fail("Agent logs accessed a global database or user") monkeypatch.setattr(session_factory, "create_session", reject_global) @@ -128,7 +173,7 @@ def reject_global(*_args, **_kwargs): name="api", description="Search", schema="{}", - schema_type_str="openapi", + schema_type_str=ApiProviderSchemaType.OPENAPI, icon='{"content":"S","background":"#000"}', tools_str="[]", credentials_str="{}", @@ -158,13 +203,14 @@ def reject_global(*_args, **_kwargs): engine.dispose() -def get_log(service, context=CONTEXT, **kwargs): - return service.get( +def get_log(service: AgentLogService, context: RequestContext = CONTEXT, **kwargs: str) -> AgentLogResult: + result = service.get( context, **{"app_id": "app", "conversation_id": "conversation", "message_id": "message", **kwargs} ) + return AgentLogResult(meta=result["meta"], iterations=result["iterations"], files=result["files"]) -def test_log_uses_loaded_timezone_configuration_and_tool_metadata(store): +def test_log_uses_loaded_timezone_configuration_and_tool_metadata(store: AgentLogStore) -> None: assert not has_app_context() sessions, service = store result = get_log(service) @@ -197,20 +243,31 @@ def test_log_uses_loaded_timezone_configuration_and_tool_metadata(store): @pytest.mark.parametrize("failure", ["workspace", "mode", "conversation", "message", "config"]) -def test_log_enforces_complete_owner_chain(store, failure): +def test_log_enforces_complete_owner_chain( + store: AgentLogStore, + failure: Literal["workspace", "mode", "conversation", "message", "config"], +) -> None: sessions, service = store context = CONTEXT with sessions.begin() as session: if failure == "workspace": context = CONTEXT._replace(active_workspace_id="foreign") elif failure == "mode": - session.get(App, "app").mode = AppMode.WORKFLOW + app = session.get(App, "app") + assert app is not None + app.mode = AppMode.WORKFLOW elif failure == "conversation": - session.get(Conversation, "conversation").app_id = "another-app" + conversation = session.get(Conversation, "conversation") + assert conversation is not None + conversation.app_id = "another-app" elif failure == "message": - session.get(Message, "message").app_id = "another-app" + message = session.get(Message, "message") + assert message is not None + message.app_id = "another-app" else: - session.get(App, "app").app_model_config_id = None + app = session.get(App, "app") + assert app is not None + app.app_model_config_id = None error = ( AgentLogAppNotFoundError if failure in {"workspace", "mode"} @@ -223,11 +280,13 @@ def test_log_enforces_complete_owner_chain(store, failure): @pytest.mark.parametrize( "file_type", [FileTransferMethod.LOCAL_FILE, FileTransferMethod.REMOTE_URL, FileTransferMethod.TOOL_FILE] ) -def test_log_releases_read_transaction_before_file_processing(store, monkeypatch, file_type): +def test_log_releases_read_transaction_before_file_processing( + store: AgentLogStore, monkeypatch: pytest.MonkeyPatch, file_type: FileTransferMethod +) -> None: sessions, service = store - remote_calls = [] + remote_calls: list[str] = [] - def remote_info(url): + def remote_info(url: str) -> tuple[str, str, int]: assert sessions.kw["bind"].pool.checkedout() == 0 remote_calls.append(url) return "text/plain", "remote.txt", 42 @@ -252,7 +311,7 @@ def remote_info(url): session.add( MessageFile( message_id="message", - type="document", + type=FileType.DOCUMENT, transfer_method=file_type, url="https://example.com/remote.txt" if file_type == FileTransferMethod.REMOTE_URL @@ -278,14 +337,18 @@ def remote_info(url): with sessions() as session: file = session.scalar(select(MessageFile)) if file_type == FileTransferMethod.TOOL_FILE: + assert file is not None assert file.upload_file_id is None # Viewing a log does not rewrite attachment records. -def test_log_handles_missing_executor_and_malformed_thought_metadata(store): +def test_log_handles_missing_executor_and_malformed_thought_metadata(store: AgentLogStore) -> None: sessions, service = store with sessions.begin() as session: - session.get(Conversation, "conversation").from_account_id = "deleted" + conversation = session.get(Conversation, "conversation") + assert conversation is not None + conversation.from_account_id = "deleted" thought = session.scalar(select(MessageAgentThought)) + assert thought is not None thought.tool_labels_str = thought.tool_meta_str = thought.tool_input = thought.observation = "invalid-json" result = get_log(service) assert result["meta"]["executor"] == "Unknown" @@ -296,7 +359,7 @@ def test_log_handles_missing_executor_and_malformed_thought_metadata(store): @pytest.mark.parametrize("tenant_id", ["tenant", "foreign"]) -def test_log_resolves_end_user_inside_workspace(store, tenant_id): +def test_log_resolves_end_user_inside_workspace(store: AgentLogStore, tenant_id: Literal["tenant", "foreign"]) -> None: sessions, service = store with sessions.begin() as session: user = EndUser( @@ -305,15 +368,17 @@ def test_log_resolves_end_user_inside_workspace(store, tenant_id): session.add(user) session.flush() conversation = session.get(Conversation, "conversation") + assert conversation is not None conversation.from_account_id = None conversation.from_end_user_id = user.id assert get_log(service)["meta"]["executor"] == ("Customer" if tenant_id == "tenant" else "Unknown") -def test_log_preserves_tool_errors_and_empty_iterations(store): +def test_log_preserves_tool_errors_and_empty_iterations(store: AgentLogStore) -> None: sessions, service = store with sessions.begin() as session: thought = session.scalar(select(MessageAgentThought)) + assert thought is not None thought.tool_meta_str = '{"search":{"error":"Remote unavailable","time_cost":2}}' thought.tokens = None result = get_log(service) @@ -321,15 +386,19 @@ def test_log_preserves_tool_errors_and_empty_iterations(store): tool = result["iterations"][0]["tool_calls"][0] assert (tool["status"], tool["error"], tool["time_cost"]) == ("error", "Remote unavailable", 2) with sessions.begin() as session: - session.delete(session.scalar(select(MessageAgentThought))) + thought = session.scalar(select(MessageAgentThought)) + assert thought is not None + session.delete(thought) result = get_log(service) assert result["meta"]["iterations"] == 0 assert result["iterations"] == [] -def test_log_rejects_disabled_agent_configuration(store): +def test_log_rejects_disabled_agent_configuration(store: AgentLogStore) -> None: sessions, service = store with sessions.begin() as session: - session.scalar(select(AppModelConfig)).agent_mode = '{"enabled":false}' + config = session.scalar(select(AppModelConfig)) + assert config is not None + config.agent_mode = '{"enabled":false}' with pytest.raises(AgentLogConfigurationError, match="Agent config not found"): get_log(service) From d15d6efd898665a9bbcb8b86330a6ae4739c8aba Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Fri, 9 Oct 2026 12:55:15 +0800 Subject: [PATCH 3/8] docs(api): regenerate agent log endpoint descriptions --- api/openapi/markdown/console-openapi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index 77599b37080a6c..832cb46dcf770a 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -15517,10 +15517,10 @@ Legacy Chat App model config used only for follow-up question generation. | status | string | | Yes | | time_cost | number
integer | | Yes | | tool_icon | | | No | -| tool_input | object | | Yes | +| tool_input | | | Yes | | tool_label | string | | Yes | | tool_name | string | | Yes | -| tool_output | object | | Yes | +| tool_output | | | Yes | | tool_parameters | object | | Yes | #### AgentUserSatisfactionRateStatisticResponse From 2e821a2cae36b65b3c70b3c7c64754c17fa7c11e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 04:58:04 +0000 Subject: [PATCH 4/8] [autofix.ci] apply automated fixes --- .../contracts/generated/api/console/apps/types.gen.ts | 8 ++------ packages/contracts/generated/api/console/apps/zod.gen.ts | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/contracts/generated/api/console/apps/types.gen.ts b/packages/contracts/generated/api/console/apps/types.gen.ts index 2d4fbb1b6c22ad..0ad4e84a005a7d 100644 --- a/packages/contracts/generated/api/console/apps/types.gen.ts +++ b/packages/contracts/generated/api/console/apps/types.gen.ts @@ -2496,14 +2496,10 @@ export type AgentToolCallResponse = { status: string time_cost: number | number tool_icon?: unknown - tool_input: { - [key: string]: unknown - } + tool_input: unknown tool_label: string tool_name: string - tool_output: { - [key: string]: unknown - } + tool_output: unknown tool_parameters: { [key: string]: unknown } diff --git a/packages/contracts/generated/api/console/apps/zod.gen.ts b/packages/contracts/generated/api/console/apps/zod.gen.ts index eb7445c1fc7415..72bb127ae23034 100644 --- a/packages/contracts/generated/api/console/apps/zod.gen.ts +++ b/packages/contracts/generated/api/console/apps/zod.gen.ts @@ -2195,10 +2195,10 @@ export const zAgentToolCallResponse = z.object({ status: z.string(), time_cost: z.union([z.number(), z.int()]), tool_icon: z.unknown().optional(), - tool_input: z.record(z.string(), z.unknown()), + tool_input: z.unknown(), tool_label: z.string(), tool_name: z.string(), - tool_output: z.record(z.string(), z.unknown()), + tool_output: z.unknown(), tool_parameters: z.record(z.string(), z.unknown()), }) From 566ec929a76f89ec675d35e9adbae61bed90e1b8 Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Thu, 15 Oct 2026 16:26:29 +0800 Subject: [PATCH 5/8] fix(workflow): address review findings in #43688 --- api/controllers/console/app/agent.py | 2 +- api/openapi/markdown/console-openapi.md | 2 +- .../console/app/test_agent_logs.py | 50 +++++++++++++------ .../services/agent/test_log_service.py | 6 +-- .../generated/api/console/apps/types.gen.ts | 6 ++- .../generated/api/console/apps/zod.gen.ts | 2 +- 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/api/controllers/console/app/agent.py b/api/controllers/console/app/agent.py index 33571658f14395..c0d50c481fee0b 100644 --- a/api/controllers/console/app/agent.py +++ b/api/controllers/console/app/agent.py @@ -45,7 +45,7 @@ class AgentToolCallResponse(ResponseModel): error: str | None = None time_cost: float | int tool_name: str - tool_label: str + tool_label: str | dict[str, str] tool_input: Any tool_output: Any tool_parameters: dict[str, Any] diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index 832cb46dcf770a..4d2f256459c43c 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -15518,7 +15518,7 @@ Legacy Chat App model config used only for follow-up question generation. | time_cost | number
integer | | Yes | | tool_icon | | | No | | tool_input | | | Yes | -| tool_label | string | | Yes | +| tool_label | string
object | | Yes | | tool_name | string | | Yes | | tool_output | | | Yes | | tool_parameters | object | | Yes | diff --git a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py index 502dbc7ae81e52..a48a4ecd42ad57 100644 --- a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py +++ b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py @@ -30,6 +30,7 @@ def response_mapping(value: object) -> dict[str, object]: @dataclass class LogService: error: Exception | None = None + tool_label: str | dict[str, str] = "Search" calls: list[LogCall] = field(default_factory=list) def get( @@ -68,7 +69,7 @@ def get( "status": "success", "time_cost": 0, "tool_name": "search", - "tool_label": "Search", + "tool_label": self.tool_label, "tool_input": dict[str, object](), "tool_output": "text output", "tool_parameters": dict[str, object](), @@ -90,23 +91,44 @@ class Services: agent_apps: AgentServices -@pytest.mark.parametrize("missing", [False, True]) -def test_log_controller_delegates_context_and_serializes(monkeypatch: pytest.MonkeyPatch, missing: bool) -> None: - logs = LogService(AgentLogAppNotFoundError() if missing else None) +@pytest.mark.parametrize( + "tool_label", + [ + "Search", + {"en_US": "Search", "zh_Hans": "搜索"}, + ], +) +def test_log_controller_delegates_context_and_serializes( + monkeypatch: pytest.MonkeyPatch, tool_label: str | dict[str, str] +) -> None: + logs = LogService(tool_label=tool_label) monkeypatch.setattr(agent, "application_services", lambda: Services(AgentServices(logs))) endpoint = agent.AgentLogApi() method = unwrap(endpoint.get) payload = agent.AgentLogQuery(message_id=MESSAGE_ID, conversation_id=CONVERSATION_ID) - if missing: - with pytest.raises(AppNotFoundError): - method(endpoint, payload, CONTEXT, APP_ID) - else: - response = response_mapping(method(endpoint, payload, CONTEXT, APP_ID)) - iterations = response["iterations"] - assert isinstance(iterations, list) - tool_calls = response_mapping(iterations[0])["tool_calls"] - assert isinstance(tool_calls, list) - assert response_mapping(tool_calls[0])["tool_output"] == "text output" + response = response_mapping(method(endpoint, payload, CONTEXT, APP_ID)) + iterations = response["iterations"] + assert isinstance(iterations, list) + tool_calls = response_mapping(iterations[0])["tool_calls"] + assert isinstance(tool_calls, list) + tool_call = response_mapping(tool_calls[0]) + assert tool_call["tool_label"] == tool_label + assert tool_call["tool_output"] == "text output" + assert logs.calls == [ + (CONTEXT, {"app_id": str(APP_ID), "message_id": MESSAGE_ID, "conversation_id": CONVERSATION_ID}) + ] + + +def test_log_controller_translates_missing_app(monkeypatch: pytest.MonkeyPatch) -> None: + logs = LogService(error=AgentLogAppNotFoundError()) + monkeypatch.setattr(agent, "application_services", lambda: Services(AgentServices(logs))) + endpoint = agent.AgentLogApi() + method = unwrap(endpoint.get) + payload = agent.AgentLogQuery(message_id=MESSAGE_ID, conversation_id=CONVERSATION_ID) + + with pytest.raises(AppNotFoundError): + method(endpoint, payload, CONTEXT, APP_ID) + assert logs.calls == [ (CONTEXT, {"app_id": str(APP_ID), "message_id": MESSAGE_ID, "conversation_id": CONVERSATION_ID}) ] diff --git a/api/tests/unit_tests/services/agent/test_log_service.py b/api/tests/unit_tests/services/agent/test_log_service.py index baea8ae1c75c0a..0e8b3006229c7c 100644 --- a/api/tests/unit_tests/services/agent/test_log_service.py +++ b/api/tests/unit_tests/services/agent/test_log_service.py @@ -48,7 +48,7 @@ class AgentToolCall(TypedDict): error: object | None time_cost: float | int tool_name: str - tool_label: str + tool_label: str | dict[str, str] tool_input: object tool_output: object tool_parameters: dict[str, object] @@ -188,7 +188,7 @@ def reject_global(*_args: object, **_kwargs: object) -> None: created_by="viewer", thought="Search first", tool="search", - tool_labels_str='{"search":"Search"}', + tool_labels_str='{"search":{"en_US":"Search","zh_Hans":"搜索"}}', tokens=10, tool_meta_str='{"search":{"tool_parameters":{"limit":3},"time_cost":1.2}}', tool_input='{"query":"test"}', @@ -232,7 +232,7 @@ def test_log_uses_loaded_timezone_configuration_and_tool_metadata(store: AgentLo "error": None, "time_cost": 1.2, "tool_name": "search", - "tool_label": "Search", + "tool_label": {"en_US": "Search", "zh_Hans": "搜索"}, "tool_input": {"query": "test"}, "tool_output": {"result": "found"}, "tool_parameters": {"limit": 3}, diff --git a/packages/contracts/generated/api/console/apps/types.gen.ts b/packages/contracts/generated/api/console/apps/types.gen.ts index 0ad4e84a005a7d..4536643962184a 100644 --- a/packages/contracts/generated/api/console/apps/types.gen.ts +++ b/packages/contracts/generated/api/console/apps/types.gen.ts @@ -2497,7 +2497,11 @@ export type AgentToolCallResponse = { time_cost: number | number tool_icon?: unknown tool_input: unknown - tool_label: string + tool_label: + | string + | { + [key: string]: string + } tool_name: string tool_output: unknown tool_parameters: { diff --git a/packages/contracts/generated/api/console/apps/zod.gen.ts b/packages/contracts/generated/api/console/apps/zod.gen.ts index 72bb127ae23034..8f5516be6fc69b 100644 --- a/packages/contracts/generated/api/console/apps/zod.gen.ts +++ b/packages/contracts/generated/api/console/apps/zod.gen.ts @@ -2196,7 +2196,7 @@ export const zAgentToolCallResponse = z.object({ time_cost: z.union([z.number(), z.int()]), tool_icon: z.unknown().optional(), tool_input: z.unknown(), - tool_label: z.string(), + tool_label: z.union([z.string(), z.record(z.string(), z.string())]), tool_name: z.string(), tool_output: z.unknown(), tool_parameters: z.record(z.string(), z.unknown()), From acfcf2c1b59a89ebc329a7df2f6459d88d8c5382 Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Mon, 26 Oct 2026 20:38:38 +0800 Subject: [PATCH 6/8] Use relocated helpers in Agent logs tests --- api/tests/unit_tests/controllers/console/app/test_agent_logs.py | 2 +- api/tests/unit_tests/services/agent/test_log_service.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py index a48a4ecd42ad57..9445e1c3274866 100644 --- a/api/tests/unit_tests/controllers/console/app/test_agent_logs.py +++ b/api/tests/unit_tests/controllers/console/app/test_agent_logs.py @@ -12,7 +12,7 @@ from controllers.console.wraps import RBACPermission from machinery.context import RequestContext from services.agent.log_contracts import AgentLogAppNotFoundError -from tests.unit_tests.controllers.rbac_introspection import rbac_checks +from tests.helpers.unit.controllers.rbac_introspection import rbac_checks CONTEXT = RequestContext("request", "trace", "viewer", "tenant") APP_ID = UUID("00000000-0000-0000-0000-000000000001") diff --git a/api/tests/unit_tests/services/agent/test_log_service.py b/api/tests/unit_tests/services/agent/test_log_service.py index 0e8b3006229c7c..e34495bc3c66ef 100644 --- a/api/tests/unit_tests/services/agent/test_log_service.py +++ b/api/tests/unit_tests/services/agent/test_log_service.py @@ -36,7 +36,7 @@ from models.tools import ApiToolProvider, ToolFile from services.agent.log_contracts import AgentLogAppNotFoundError, AgentLogConfigurationError, AgentLogNotFoundError from services.agent.log_service import AgentLogService -from tests.unit_tests.model_factories import make_account, make_app, make_conversation, make_message, make_upload_file +from tests.helpers.unit.model_factories import make_account, make_app, make_conversation, make_message, make_upload_file CONTEXT = RequestContext("request", None, "viewer", "tenant") From 553ab2d266aa1544f64027fa94208f6790f70780 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 05:31:52 +0000 Subject: [PATCH 7/8] [autofix.ci] apply automated fixes --- .../generated/api/console/apps/errors.gen.ts | 26 ------------------- .../generated/api/console/errors.gen.ts | 20 -------------- .../generated/api/console/rag/errors.gen.ts | 18 ------------- 3 files changed, 64 deletions(-) diff --git a/packages/contracts/generated/api/console/apps/errors.gen.ts b/packages/contracts/generated/api/console/apps/errors.gen.ts index 1ca81cfa7c000b..4cd4d5e228e674 100644 --- a/packages/contracts/generated/api/console/apps/errors.gen.ts +++ b/packages/contracts/generated/api/console/apps/errors.gen.ts @@ -605,8 +605,6 @@ export const get15Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, } -export const get16Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post13Errors = { no_file_uploaded: { status: 400, data: zNoFileUploadedError }, too_many_files: { status: 400, data: zTooManyFilesError }, @@ -919,17 +917,10 @@ export const get65Errors = { app_not_found: { status: 404, data: zAppNotFoundErr export const get66Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } -export const get67Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const post44Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const get68Errors = { - app_not_found: { status: 404, data: zAppNotFoundError }, draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, } -export const post45Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post46Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } export const post47Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } @@ -993,10 +984,6 @@ export const post56Errors = { app_not_found: { status: 404, data: zAppNotFoundEr export const post57Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } -export const delete11Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const get72Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post58Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, rate_limit_error: { status: 429, data: zRateLimitError }, @@ -1025,8 +1012,6 @@ export const get76Errors = { workflow_run_not_found: { status: 404, data: zWorkflowRunNotFoundError }, } -export const get77Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post59Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, rate_limit_error: { status: 429, data: zRateLimitError }, @@ -1037,18 +1022,7 @@ export const post60Errors = { rate_limit_error: { status: 429, data: zRateLimitError }, } -export const put6Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const delete12Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const get78Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const patch2Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const delete13Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const get79Errors = { - app_not_found: { status: 404, data: zAppNotFoundError }, draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, } diff --git a/packages/contracts/generated/api/console/errors.gen.ts b/packages/contracts/generated/api/console/errors.gen.ts index 34e0844610ffc4..e3098084a0a65e 100644 --- a/packages/contracts/generated/api/console/errors.gen.ts +++ b/packages/contracts/generated/api/console/errors.gen.ts @@ -1,11 +1,8 @@ // This file is auto-generated by scripts/add-orpc-error-maps.ts export { delete10Errors as 'DELETE /apps/{app_id}/workflow/comments/{comment_id}' } from './apps/errors.gen.ts' export { delete10Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/research-tasks/{task_id}' } from './knowledge-fs/errors.gen.ts' -export { delete11Errors as 'DELETE /apps/{app_id}/workflows/draft/nodes/{node_id}/variables' } from './apps/errors.gen.ts' export { delete11Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/sources/{source_id}' } from './knowledge-fs/errors.gen.ts' -export { delete12Errors as 'DELETE /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { delete12Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}' } from './knowledge-fs/errors.gen.ts' -export { delete13Errors as 'DELETE /apps/{app_id}/workflows/draft/variables' } from './apps/errors.gen.ts' export { delete13Errors as 'DELETE /knowledge-fs/uploads/{upload_id}' } from './knowledge-fs/errors.gen.ts' export { delete14Errors as 'DELETE /apps/{app_id}/workflows/{workflow_id}' } from './apps/errors.gen.ts' export { delete16Errors as 'DELETE /workspaces/current/skills/{skill_id}' } from './workspaces/errors.gen.ts' @@ -14,15 +11,12 @@ export { delete2Errors as 'DELETE /apps/{app_id}/agent/config/skills/{name}' } f export { delete2Errors as 'DELETE /datasets/external-knowledge-api/{external_knowledge_api_id}' } from './datasets/errors.gen.ts' export { delete2Errors as 'DELETE /installed-apps/{installed_app_id}/saved-messages/{message_id}' } from './installed-apps/errors.gen.ts' export { delete2Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/app-bindings/{caller_kind}/{app_id}' } from './knowledge-fs/errors.gen.ts' -export { delete2Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/variables' } from './rag/errors.gen.ts' export { delete3Errors as 'DELETE /agent/{agent_id}/config/files/{name}' } from './agent/errors.gen.ts' export { delete3Errors as 'DELETE /datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' } from './datasets/errors.gen.ts' export { delete3Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/documents/bulk' } from './knowledge-fs/errors.gen.ts' -export { delete3Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { delete4Errors as 'DELETE /agent/{agent_id}/config/skills/{name}' } from './agent/errors.gen.ts' export { delete4Errors as 'DELETE /apps/{app_id}/annotations' } from './apps/errors.gen.ts' export { delete4Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/documents/{document_id}' } from './knowledge-fs/errors.gen.ts' -export { delete4Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/variables' } from './rag/errors.gen.ts' export { delete4Errors as 'DELETE /workspaces/current/members/{member_id}' } from './workspaces/errors.gen.ts' export { delete5Errors as 'DELETE /agent/{agent_id}/trace-config' } from './agent/errors.gen.ts' export { delete5Errors as 'DELETE /apps/{app_id}/chat-conversations/{conversation_id}' } from './apps/errors.gen.ts' @@ -63,7 +57,6 @@ export { get13Errors as 'GET /trial-apps/{app_id}/messages/{message_id}/suggeste export { get14Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/content' } from './agent/errors.gen.ts' export { get14Errors as 'GET /apps/{app_id}/agent/config/skills/{name}/inspect' } from './apps/errors.gen.ts' export { get14Errors as 'GET /knowledge-fs/spaces/{control_space_id}/documents/{document_id}/revisions' } from './knowledge-fs/errors.gen.ts' -export { get14Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/variables' } from './rag/errors.gen.ts' export { get14Errors as 'GET /snippets/{snippet_id}/workflows/draft/variables' } from './snippets/errors.gen.ts' export { get14Errors as 'GET /trial-apps/{app_id}/parameters' } from './trial-apps/errors.gen.ts' export { get15Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/download' } from './agent/errors.gen.ts' @@ -73,19 +66,16 @@ export { get15Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/pre-pr export { get15Errors as 'GET /snippets/{snippet_id}/workflows/draft' } from './snippets/errors.gen.ts' export { get15Errors as 'GET /trial-apps/{app_id}/site' } from './trial-apps/errors.gen.ts' export { get16Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/preview' } from './agent/errors.gen.ts' -export { get16Errors as 'GET /apps/{app_id}/agent/logs' } from './apps/errors.gen.ts' export { get16Errors as 'GET /datasets/{dataset_id}/documents/{document_id}/indexing-estimate' } from './datasets/errors.gen.ts' export { get16Errors as 'GET /knowledge-fs/spaces/{control_space_id}/documents' } from './knowledge-fs/errors.gen.ts' export { get16Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/processing/parameters' } from './rag/errors.gen.ts' export { get16Errors as 'GET /trial-apps/{app_id}/workflows' } from './trial-apps/errors.gen.ts' export { get17Errors as 'GET /agent/{agent_id}/config/skills/{name}/inspect' } from './agent/errors.gen.ts' export { get17Errors as 'GET /knowledge-fs/spaces/{control_space_id}/external-access' } from './knowledge-fs/errors.gen.ts' -export { get17Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/system-variables' } from './rag/errors.gen.ts' export { get17Errors as 'GET /trial-apps/{app_id}' } from './trial-apps/errors.gen.ts' export { get18Errors as 'GET /agent/{agent_id}/config/skills' } from './agent/errors.gen.ts' export { get18Errors as 'GET /datasets/{dataset_id}/documents/{document_id}/notion/sync' } from './datasets/errors.gen.ts' export { get18Errors as 'GET /knowledge-fs/spaces/{control_space_id}/golden-questions' } from './knowledge-fs/errors.gen.ts' -export { get18Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { get19Errors as 'GET /agent/{agent_id}/log-sources' } from './agent/errors.gen.ts' export { get19Errors as 'GET /knowledge-fs/spaces/{control_space_id}/jobs/{job_id}' } from './knowledge-fs/errors.gen.ts' export { get19Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/variables' } from './rag/errors.gen.ts' @@ -228,7 +218,6 @@ export { get63Errors as 'GET /apps/{app_id}/workflow/statistics/daily-terminals' export { get64Errors as 'GET /apps/{app_id}/workflow/statistics/token-costs' } from './apps/errors.gen.ts' export { get65Errors as 'GET /apps/{app_id}/workflows/default-workflow-block-configs/{block_type}' } from './apps/errors.gen.ts' export { get66Errors as 'GET /apps/{app_id}/workflows/default-workflow-block-configs' } from './apps/errors.gen.ts' -export { get67Errors as 'GET /apps/{app_id}/workflows/draft/conversation-variables' } from './apps/errors.gen.ts' export { get68Errors as 'GET /apps/{app_id}/workflows/draft/environment-variables' } from './apps/errors.gen.ts' export { get69Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/candidates' } from './apps/errors.gen.ts' export { get6Errors as 'GET /agent/{agent_id}/chat-messages' } from './agent/errors.gen.ts' @@ -239,13 +228,10 @@ export { get6Errors as 'GET /rag/pipelines/{pipeline_id}/exports' } from './rag/ export { get6Errors as 'GET /trial-apps/{app_id}/agent/config/skills/{name}/files/content' } from './trial-apps/errors.gen.ts' export { get70Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './apps/errors.gen.ts' export { get71Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/last-run' } from './apps/errors.gen.ts' -export { get72Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/variables' } from './apps/errors.gen.ts' export { get73Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/events' } from './apps/errors.gen.ts' export { get74Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/{node_id}/{output_name}/preview' } from './apps/errors.gen.ts' export { get75Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/{node_id}' } from './apps/errors.gen.ts' export { get76Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs' } from './apps/errors.gen.ts' -export { get77Errors as 'GET /apps/{app_id}/workflows/draft/system-variables' } from './apps/errors.gen.ts' -export { get78Errors as 'GET /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { get79Errors as 'GET /apps/{app_id}/workflows/draft/variables' } from './apps/errors.gen.ts' export { get7Errors as 'GET /account/profile' } from './account/errors.gen.ts' export { get7Errors as 'GET /agent/{agent_id}/composer/candidates' } from './agent/errors.gen.ts' @@ -287,11 +273,9 @@ export { getErrors as 'GET /installed-apps/{installed_app_id}/conversations' } f export { getErrors as 'GET /remote-files/{url}' } from './remote-files/errors.gen.ts' export { getErrors as 'GET /trial-apps/{app_id}/agent-composer' } from './trial-apps/errors.gen.ts' export { getErrors as 'GET /website/crawl/status/{job_id}' } from './website/errors.gen.ts' -export { patch2Errors as 'PATCH /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { patch2Errors as 'PATCH /datasets/{dataset_id}/documents/status/{action}/batch' } from './datasets/errors.gen.ts' export { patch2Errors as 'PATCH /installed-apps/{installed_app_id}/conversations/{c_id}/unpin' } from './installed-apps/errors.gen.ts' export { patch2Errors as 'PATCH /knowledge-fs/spaces/{control_space_id}/golden-questions/{question_id}' } from './knowledge-fs/errors.gen.ts' -export { patch2Errors as 'PATCH /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { patch3Errors as 'PATCH /apps/{app_id}/workflows/{workflow_id}' } from './apps/errors.gen.ts' export { patch3Errors as 'PATCH /datasets/{dataset_id}/documents/{document_id}/processing/pause' } from './datasets/errors.gen.ts' export { patch3Errors as 'PATCH /installed-apps/{installed_app_id}' } from './installed-apps/errors.gen.ts' @@ -452,9 +436,7 @@ export { post42Errors as 'POST /apps/{app_id}/workflow/comments/{comment_id}/res export { post42Errors as 'POST /knowledge-fs/uploads' } from './knowledge-fs/errors.gen.ts' export { post43Errors as 'POST /apps/{app_id}/workflow/comments' } from './apps/errors.gen.ts' export { post43Errors as 'POST /workspaces/current/plugin/upload/github' } from './workspaces/errors.gen.ts' -export { post44Errors as 'POST /apps/{app_id}/workflows/draft/conversation-variables' } from './apps/errors.gen.ts' export { post44Errors as 'POST /workspaces/current/plugin/upload/pkg' } from './workspaces/errors.gen.ts' -export { post45Errors as 'POST /apps/{app_id}/workflows/draft/environment-variables' } from './apps/errors.gen.ts' export { post46Errors as 'POST /apps/{app_id}/workflows/draft/features' } from './apps/errors.gen.ts' export { post47Errors as 'POST /apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/delivery-test' } from './apps/errors.gen.ts' export { post48Errors as 'POST /apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/form/preview' } from './apps/errors.gen.ts' @@ -559,10 +541,8 @@ export { put4Errors as 'PUT /apps/{app_id}/workflow/comments/{comment_id}' } fro export { put4Errors as 'PUT /knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy' } from './knowledge-fs/errors.gen.ts' export { put5Errors as 'PUT /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './apps/errors.gen.ts' export { put5Errors as 'PUT /knowledge-fs/spaces/{control_space_id}/tags' } from './knowledge-fs/errors.gen.ts' -export { put6Errors as 'PUT /apps/{app_id}/workflows/draft/variables/{variable_id}/reset' } from './apps/errors.gen.ts' export { putErrors as 'PUT /agent/{agent_id}/build-draft' } from './agent/errors.gen.ts' export { putErrors as 'PUT /apps/{app_id}/network-access-group' } from './apps/errors.gen.ts' export { putErrors as 'PUT /knowledge-fs/spaces/{control_space_id}/app-bindings' } from './knowledge-fs/errors.gen.ts' -export { putErrors as 'PUT /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}/reset' } from './rag/errors.gen.ts' export { putErrors as 'PUT /snippets/{snippet_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './snippets/errors.gen.ts' export { putErrors as 'PUT /workspaces/current/agents/{agent_id}/skills' } from './workspaces/errors.gen.ts' diff --git a/packages/contracts/generated/api/console/rag/errors.gen.ts b/packages/contracts/generated/api/console/rag/errors.gen.ts index f87498e70afbc6..501d339d7f2b47 100644 --- a/packages/contracts/generated/api/console/rag/errors.gen.ts +++ b/packages/contracts/generated/api/console/rag/errors.gen.ts @@ -78,7 +78,6 @@ export const post10Errors = { pipeline_not_found: { status: 404, data: zPipeline export const get12Errors = { draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, - pipeline_not_found: { status: 404, data: zPipelineNotFoundError }, } export const post11Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } @@ -89,10 +88,6 @@ export const get13Errors = { pipeline_not_found: { status: 404, data: zPipelineN export const post13Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } -export const delete2Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const get14Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - export const get15Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } export const get16Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } @@ -102,21 +97,8 @@ export const post14Errors = { rate_limit_error: { status: 429, data: zRateLimitError }, } -export const get17Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const putErrors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const delete3Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const get18Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const patch2Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - -export const delete4Errors = { pipeline_not_found: { status: 404, data: zPipelineNotFoundError } } - export const get19Errors = { draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, - pipeline_not_found: { status: 404, data: zPipelineNotFoundError }, } export const get20Errors = { From c1dcbcaf5e1a27150240e3d0416240f6b81e0982 Mon Sep 17 00:00:00 2001 From: Byron Wang Date: Sat, 31 Oct 2026 21:37:42 +0800 Subject: [PATCH 8/8] Regenerate API error contracts for migrated Agent logs --- .../generated/api/console/apps/errors.gen.ts | 26 ------------------- .../generated/api/console/errors.gen.ts | 20 -------------- 2 files changed, 46 deletions(-) diff --git a/packages/contracts/generated/api/console/apps/errors.gen.ts b/packages/contracts/generated/api/console/apps/errors.gen.ts index fe263698edf6ed..4aa9548e94fe18 100644 --- a/packages/contracts/generated/api/console/apps/errors.gen.ts +++ b/packages/contracts/generated/api/console/apps/errors.gen.ts @@ -709,8 +709,6 @@ export const get15Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, } -export const get16Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post15Errors = { no_file_uploaded: { status: 400, data: zNoFileUploadedError }, too_many_files: { status: 400, data: zTooManyFilesError }, @@ -1067,17 +1065,10 @@ export const get65Errors = { app_not_found: { status: 404, data: zAppNotFoundErr export const get66Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } -export const get67Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const post46Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const get68Errors = { - app_not_found: { status: 404, data: zAppNotFoundError }, draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, } -export const post47Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post48Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } export const post49Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } @@ -1143,10 +1134,6 @@ export const post59Errors = { app_not_found: { status: 404, data: zAppNotFoundEr export const post60Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } -export const delete11Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const get72Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post61Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, rate_limit_error: { status: 429, data: zRateLimitError }, @@ -1175,8 +1162,6 @@ export const get76Errors = { workflow_run_not_found: { status: 404, data: zWorkflowRunNotFoundError }, } -export const get77Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const post62Errors = { app_not_found: { status: 404, data: zAppNotFoundError }, rate_limit_error: { status: 429, data: zRateLimitError }, @@ -1187,18 +1172,7 @@ export const post63Errors = { rate_limit_error: { status: 429, data: zRateLimitError }, } -export const put6Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const delete12Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const get78Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const patch2Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - -export const delete13Errors = { app_not_found: { status: 404, data: zAppNotFoundError } } - export const get79Errors = { - app_not_found: { status: 404, data: zAppNotFoundError }, draft_workflow_not_exist: { status: 404, data: zDraftWorkflowNotExistError }, } diff --git a/packages/contracts/generated/api/console/errors.gen.ts b/packages/contracts/generated/api/console/errors.gen.ts index a76961cb811b58..fd47e4bbf7cf59 100644 --- a/packages/contracts/generated/api/console/errors.gen.ts +++ b/packages/contracts/generated/api/console/errors.gen.ts @@ -1,12 +1,9 @@ // This file is auto-generated by scripts/add-orpc-error-maps.ts export { delete10Errors as 'DELETE /apps/{app_id}/workflow/comments/{comment_id}' } from './apps/errors.gen.ts' export { delete10Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/research-tasks/{task_id}' } from './knowledge-fs/errors.gen.ts' -export { delete11Errors as 'DELETE /apps/{app_id}/workflows/draft/nodes/{node_id}/variables' } from './apps/errors.gen.ts' export { delete11Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/sources/{source_id}' } from './knowledge-fs/errors.gen.ts' -export { delete12Errors as 'DELETE /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { delete12Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}' } from './knowledge-fs/errors.gen.ts' export { delete12Errors as 'DELETE /workspaces/current/network-access-groups/{group_id}' } from './workspaces/errors.gen.ts' -export { delete13Errors as 'DELETE /apps/{app_id}/workflows/draft/variables' } from './apps/errors.gen.ts' export { delete13Errors as 'DELETE /knowledge-fs/uploads/{upload_id}' } from './knowledge-fs/errors.gen.ts' export { delete14Errors as 'DELETE /apps/{app_id}/workflows/{workflow_id}' } from './apps/errors.gen.ts' export { delete20Errors as 'DELETE /workspaces/current/skills/{skill_id}' } from './workspaces/errors.gen.ts' @@ -15,15 +12,12 @@ export { delete2Errors as 'DELETE /apps/{app_id}/agent/config/skills/{name}' } f export { delete2Errors as 'DELETE /datasets/external-knowledge-api/{external_knowledge_api_id}' } from './datasets/errors.gen.ts' export { delete2Errors as 'DELETE /installed-apps/{installed_app_id}/saved-messages/{message_id}' } from './installed-apps/errors.gen.ts' export { delete2Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/app-bindings/{caller_kind}/{app_id}' } from './knowledge-fs/errors.gen.ts' -export { delete2Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/variables' } from './rag/errors.gen.ts' export { delete3Errors as 'DELETE /agent/{agent_id}/config/files/{name}' } from './agent/errors.gen.ts' export { delete3Errors as 'DELETE /datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' } from './datasets/errors.gen.ts' export { delete3Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/documents/bulk' } from './knowledge-fs/errors.gen.ts' -export { delete3Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { delete4Errors as 'DELETE /agent/{agent_id}/config/skills/{name}' } from './agent/errors.gen.ts' export { delete4Errors as 'DELETE /apps/{app_id}/annotations' } from './apps/errors.gen.ts' export { delete4Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/documents/{document_id}' } from './knowledge-fs/errors.gen.ts' -export { delete4Errors as 'DELETE /rag/pipelines/{pipeline_id}/workflows/draft/variables' } from './rag/errors.gen.ts' export { delete5Errors as 'DELETE /agent/{agent_id}/trace-config' } from './agent/errors.gen.ts' export { delete5Errors as 'DELETE /apps/{app_id}/chat-conversations/{conversation_id}' } from './apps/errors.gen.ts' export { delete5Errors as 'DELETE /knowledge-fs/spaces/{control_space_id}/golden-questions/{question_id}' } from './knowledge-fs/errors.gen.ts' @@ -66,7 +60,6 @@ export { get13Errors as 'GET /trial-apps/{app_id}/messages/{message_id}/suggeste export { get14Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/content' } from './agent/errors.gen.ts' export { get14Errors as 'GET /apps/{app_id}/agent/config/skills/{name}/inspect' } from './apps/errors.gen.ts' export { get14Errors as 'GET /knowledge-fs/spaces/{control_space_id}/documents/{document_id}/revisions' } from './knowledge-fs/errors.gen.ts' -export { get14Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/variables' } from './rag/errors.gen.ts' export { get14Errors as 'GET /snippets/{snippet_id}/workflows/draft/variables' } from './snippets/errors.gen.ts' export { get14Errors as 'GET /trial-apps/{app_id}/parameters' } from './trial-apps/errors.gen.ts' export { get15Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/download' } from './agent/errors.gen.ts' @@ -76,19 +69,16 @@ export { get15Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/pre-pr export { get15Errors as 'GET /snippets/{snippet_id}/workflows/draft' } from './snippets/errors.gen.ts' export { get15Errors as 'GET /trial-apps/{app_id}/site' } from './trial-apps/errors.gen.ts' export { get16Errors as 'GET /agent/{agent_id}/config/skills/{name}/files/preview' } from './agent/errors.gen.ts' -export { get16Errors as 'GET /apps/{app_id}/agent/logs' } from './apps/errors.gen.ts' export { get16Errors as 'GET /datasets/{dataset_id}/documents/{document_id}/indexing-estimate' } from './datasets/errors.gen.ts' export { get16Errors as 'GET /knowledge-fs/spaces/{control_space_id}/documents' } from './knowledge-fs/errors.gen.ts' export { get16Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/processing/parameters' } from './rag/errors.gen.ts' export { get16Errors as 'GET /trial-apps/{app_id}/workflows' } from './trial-apps/errors.gen.ts' export { get17Errors as 'GET /agent/{agent_id}/config/skills/{name}/inspect' } from './agent/errors.gen.ts' export { get17Errors as 'GET /knowledge-fs/spaces/{control_space_id}/external-access' } from './knowledge-fs/errors.gen.ts' -export { get17Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/system-variables' } from './rag/errors.gen.ts' export { get17Errors as 'GET /trial-apps/{app_id}' } from './trial-apps/errors.gen.ts' export { get18Errors as 'GET /agent/{agent_id}/config/skills' } from './agent/errors.gen.ts' export { get18Errors as 'GET /datasets/{dataset_id}/documents/{document_id}/notion/sync' } from './datasets/errors.gen.ts' export { get18Errors as 'GET /knowledge-fs/spaces/{control_space_id}/golden-questions' } from './knowledge-fs/errors.gen.ts' -export { get18Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { get19Errors as 'GET /agent/{agent_id}/log-sources' } from './agent/errors.gen.ts' export { get19Errors as 'GET /knowledge-fs/spaces/{control_space_id}/jobs/{job_id}' } from './knowledge-fs/errors.gen.ts' export { get19Errors as 'GET /rag/pipelines/{pipeline_id}/workflows/draft/variables' } from './rag/errors.gen.ts' @@ -239,7 +229,6 @@ export { get63Errors as 'GET /apps/{app_id}/workflow/statistics/daily-terminals' export { get64Errors as 'GET /apps/{app_id}/workflow/statistics/token-costs' } from './apps/errors.gen.ts' export { get65Errors as 'GET /apps/{app_id}/workflows/default-workflow-block-configs/{block_type}' } from './apps/errors.gen.ts' export { get66Errors as 'GET /apps/{app_id}/workflows/default-workflow-block-configs' } from './apps/errors.gen.ts' -export { get67Errors as 'GET /apps/{app_id}/workflows/draft/conversation-variables' } from './apps/errors.gen.ts' export { get68Errors as 'GET /apps/{app_id}/workflows/draft/environment-variables' } from './apps/errors.gen.ts' export { get69Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer/candidates' } from './apps/errors.gen.ts' export { get6Errors as 'GET /agent/{agent_id}/chat-messages' } from './agent/errors.gen.ts' @@ -250,13 +239,10 @@ export { get6Errors as 'GET /rag/pipelines/{pipeline_id}/exports' } from './rag/ export { get6Errors as 'GET /trial-apps/{app_id}/agent/config/skills/{name}/files/content' } from './trial-apps/errors.gen.ts' export { get70Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './apps/errors.gen.ts' export { get71Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/last-run' } from './apps/errors.gen.ts' -export { get72Errors as 'GET /apps/{app_id}/workflows/draft/nodes/{node_id}/variables' } from './apps/errors.gen.ts' export { get73Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/events' } from './apps/errors.gen.ts' export { get74Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/{node_id}/{output_name}/preview' } from './apps/errors.gen.ts' export { get75Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs/{node_id}' } from './apps/errors.gen.ts' export { get76Errors as 'GET /apps/{app_id}/workflows/draft/runs/{run_id}/node-outputs' } from './apps/errors.gen.ts' -export { get77Errors as 'GET /apps/{app_id}/workflows/draft/system-variables' } from './apps/errors.gen.ts' -export { get78Errors as 'GET /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { get79Errors as 'GET /apps/{app_id}/workflows/draft/variables' } from './apps/errors.gen.ts' export { get7Errors as 'GET /account/profile' } from './account/errors.gen.ts' export { get7Errors as 'GET /agent/{agent_id}/composer/candidates' } from './agent/errors.gen.ts' @@ -298,11 +284,9 @@ export { getErrors as 'GET /remote-files/{url}' } from './remote-files/errors.ge export { getErrors as 'GET /trial-apps/{app_id}/agent-composer' } from './trial-apps/errors.gen.ts' export { getErrors as 'GET /website/crawl/status/{job_id}' } from './website/errors.gen.ts' export { getErrors as 'GET /workflow-run-archives/downloads/{download_id}' } from './workflow-run-archives/errors.gen.ts' -export { patch2Errors as 'PATCH /apps/{app_id}/workflows/draft/variables/{variable_id}' } from './apps/errors.gen.ts' export { patch2Errors as 'PATCH /datasets/{dataset_id}/documents/status/{action}/batch' } from './datasets/errors.gen.ts' export { patch2Errors as 'PATCH /installed-apps/{installed_app_id}/conversations/{c_id}/unpin' } from './installed-apps/errors.gen.ts' export { patch2Errors as 'PATCH /knowledge-fs/spaces/{control_space_id}/golden-questions/{question_id}' } from './knowledge-fs/errors.gen.ts' -export { patch2Errors as 'PATCH /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}' } from './rag/errors.gen.ts' export { patch3Errors as 'PATCH /apps/{app_id}/workflows/{workflow_id}' } from './apps/errors.gen.ts' export { patch3Errors as 'PATCH /datasets/{dataset_id}/documents/{document_id}/processing/pause' } from './datasets/errors.gen.ts' export { patch3Errors as 'PATCH /installed-apps/{installed_app_id}' } from './installed-apps/errors.gen.ts' @@ -455,9 +439,7 @@ export { post44Errors as 'POST /apps/{app_id}/workflow/comments/{comment_id}/res export { post44Errors as 'POST /workspaces/current/plugin/list/installations/ids' } from './workspaces/errors.gen.ts' export { post45Errors as 'POST /apps/{app_id}/workflow/comments' } from './apps/errors.gen.ts' export { post45Errors as 'POST /workspaces/current/plugin/list/latest-versions' } from './workspaces/errors.gen.ts' -export { post46Errors as 'POST /apps/{app_id}/workflows/draft/conversation-variables' } from './apps/errors.gen.ts' export { post46Errors as 'POST /workspaces/current/plugin/parameters/dynamic-options-with-credentials' } from './workspaces/errors.gen.ts' -export { post47Errors as 'POST /apps/{app_id}/workflows/draft/environment-variables' } from './apps/errors.gen.ts' export { post48Errors as 'POST /apps/{app_id}/workflows/draft/features' } from './apps/errors.gen.ts' export { post48Errors as 'POST /workspaces/current/plugin/tasks/delete_all' } from './workspaces/errors.gen.ts' export { post49Errors as 'POST /apps/{app_id}/workflows/draft/human-input/nodes/{node_id}/delivery-test' } from './apps/errors.gen.ts' @@ -571,12 +553,10 @@ export { put4Errors as 'PUT /apps/{app_id}/workflow/comments/{comment_id}' } fro export { put4Errors as 'PUT /knowledge-fs/spaces/{control_space_id}/sources/{source_id}/sync-policy' } from './knowledge-fs/errors.gen.ts' export { put5Errors as 'PUT /apps/{app_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './apps/errors.gen.ts' export { put5Errors as 'PUT /knowledge-fs/spaces/{control_space_id}/tags' } from './knowledge-fs/errors.gen.ts' -export { put6Errors as 'PUT /apps/{app_id}/workflows/draft/variables/{variable_id}/reset' } from './apps/errors.gen.ts' export { put6Errors as 'PUT /workspaces/current/members/{member_id}/update-role' } from './workspaces/errors.gen.ts' export { put9Errors as 'PUT /workspaces/current/network-access-groups/{group_id}' } from './workspaces/errors.gen.ts' export { putErrors as 'PUT /agent/{agent_id}/build-draft' } from './agent/errors.gen.ts' export { putErrors as 'PUT /apps/{app_id}/network-access-group' } from './apps/errors.gen.ts' export { putErrors as 'PUT /knowledge-fs/spaces/{control_space_id}/app-bindings' } from './knowledge-fs/errors.gen.ts' -export { putErrors as 'PUT /rag/pipelines/{pipeline_id}/workflows/draft/variables/{variable_id}/reset' } from './rag/errors.gen.ts' export { putErrors as 'PUT /snippets/{snippet_id}/workflows/draft/nodes/{node_id}/agent-composer' } from './snippets/errors.gen.ts' export { putErrors as 'PUT /workspaces/current/agents/{agent_id}/skills' } from './workspaces/errors.gen.ts'