From 7d2f25df8e41828decb6aa7787dd20c9e23b4560 Mon Sep 17 00:00:00 2001 From: zyssyz123 <916125788@qq.com> Date: Mon, 22 Jun 2026 20:37:27 +0800 Subject: [PATCH] feat(agent): add roster service api access (#37759) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/controllers/console/agent/roster.py | 133 +++++++++- api/controllers/service_api/app/app.py | 47 +++- .../app/apps/agent_app/app_config_manager.py | 4 +- .../apps/agent_app/app_variable_projection.py | 37 +++ api/openapi/markdown/console-openapi.md | 99 ++++++++ .../console/agent/test_agent_controllers.py | 130 ++++++++++ .../controllers/service_api/app/test_app.py | 49 ++++ .../apps/agent_app/test_app_config_manager.py | 9 +- .../generated/api/console/agent/orpc.gen.ts | 231 ++++++++++++------ .../generated/api/console/agent/types.gen.ts | 122 +++++++++ .../generated/api/console/agent/zod.gen.ts | 93 +++++++ 11 files changed, 878 insertions(+), 76 deletions(-) create mode 100644 api/core/app/apps/agent_app/app_variable_projection.py diff --git a/api/controllers/console/agent/roster.py b/api/controllers/console/agent/roster.py index 14d5da7f635..fa7ab9726f6 100644 --- a/api/controllers/console/agent/roster.py +++ b/api/controllers/console/agent/roster.py @@ -3,10 +3,12 @@ from uuid import UUID from flask import abort, request from flask_restx import Resource from pydantic import AliasChoices, BaseModel, Field, field_validator +from sqlalchemy import func, select from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models from controllers.console import console_ns from controllers.console.agent.app_helpers import resolve_agent_app_model +from controllers.console.apikey import ApiKeyItem, ApiKeyList, BaseApiKeyListResource, BaseApiKeyResource from controllers.console.app.app import ( AppDetailWithSite as GenericAppDetailWithSite, ) @@ -25,9 +27,13 @@ from controllers.console.app.app import ( UpdateAppPayload as GenericUpdateAppPayload, ) from controllers.console.wraps import ( + RBACPermission, + RBACResourceScope, account_initialization_required, edit_permission_required, enterprise_license_required, + is_admin_or_owner_required, + rbac_permission_required, setup_required, with_current_tenant_id, with_current_user, @@ -49,7 +55,8 @@ from libs.datetime_utils import parse_time_range from libs.helper import dump_response from libs.login import login_required from models import Account -from models.model import IconType +from models.enums import ApiTokenType +from models.model import ApiToken, App, IconType from services.agent.errors import AgentNotFoundError from services.agent.observability_service import ( AgentLogQueryParams, @@ -103,6 +110,27 @@ class AgentAppUpdatePayload(GenericUpdateAppPayload): return role +class AgentApiStatusPayload(BaseModel): + enable_api: bool = Field(..., description="Enable or disable Agent service API") + + +class AgentApiAccessResponse(BaseModel): + enabled: bool + service_api_base_url: str + streaming_only: bool = True + chat_endpoint: str + stop_endpoint: str + conversations_endpoint: str + messages_endpoint: str + files_upload_endpoint: str + parameters_endpoint: str + info_endpoint: str + meta_endpoint: str + api_rpm: int + api_rph: int + api_key_count: int + + class AgentAppPublishedReferenceResponse(BaseModel): app_id: str app_name: str @@ -210,6 +238,7 @@ register_schema_models( console_ns, AgentAppCreatePayload, AgentAppUpdatePayload, + AgentApiStatusPayload, CopyAppPayload, AgentInviteOptionsQuery, AgentLogsQuery, @@ -221,6 +250,7 @@ register_schema_models( register_response_schema_models( console_ns, AgentAppPagination, + AgentApiAccessResponse, AgentAppPublishedReferenceResponse, AgentAppDetailWithSite, AgentAppPartial, @@ -329,6 +359,38 @@ def _resolve_agent_app_model(*, tenant_id: str, agent_id: UUID): return resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) +def _agent_api_key_count(app_id: str) -> int: + return ( + db.session.scalar( + select(func.count(ApiToken.id)).where( + ApiToken.type == ApiTokenType.APP, + ApiToken.app_id == app_id, + ) + ) + or 0 + ) + + +def _serialize_agent_api_access(app_model: App) -> dict: + base_url = app_model.api_base_url + response = AgentApiAccessResponse( + enabled=bool(app_model.enable_api), + service_api_base_url=base_url, + chat_endpoint=f"{base_url}/chat-messages", + stop_endpoint=f"{base_url}/chat-messages/{{task_id}}/stop", + conversations_endpoint=f"{base_url}/conversations", + messages_endpoint=f"{base_url}/messages", + files_upload_endpoint=f"{base_url}/files/upload", + parameters_endpoint=f"{base_url}/parameters", + info_endpoint=f"{base_url}/info", + meta_endpoint=f"{base_url}/meta", + api_rpm=app_model.api_rpm or 0, + api_rph=app_model.api_rph or 0, + api_key_count=_agent_api_key_count(str(app_model.id)), + ) + return response.model_dump(mode="json") + + def _agent_observability_service() -> AgentObservabilityService: return AgentObservabilityService(db.session) @@ -485,6 +547,75 @@ class AgentAppCopyApi(Resource): return _serialize_agent_app_detail(copied_app), 201 +@console_ns.route("/agent//api-access") +class AgentApiAccessApi(Resource): + @console_ns.response(200, "Agent service API access", console_ns.models[AgentApiAccessResponse.__name__]) + @setup_required + @login_required + @account_initialization_required + @with_current_tenant_id + def get(self, tenant_id: str, agent_id: UUID): + app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) + return _serialize_agent_api_access(app_model) + + +@console_ns.route("/agent//api-enable") +class AgentApiStatusApi(Resource): + @console_ns.expect(console_ns.models[AgentApiStatusPayload.__name__]) + @console_ns.response(200, "Agent service API status updated", console_ns.models[AgentApiAccessResponse.__name__]) + @console_ns.response(403, "Insufficient permissions") + @setup_required + @login_required + @is_admin_or_owner_required + @account_initialization_required + @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION) + @with_current_tenant_id + def post(self, tenant_id: str, agent_id: UUID): + app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) + args = AgentApiStatusPayload.model_validate(console_ns.payload) + app_model = AppService().update_app_api_status(app_model, args.enable_api) + return _serialize_agent_api_access(app_model) + + +@console_ns.route("/agent//api-keys") +class AgentApiKeyListApi(BaseApiKeyListResource): + resource_type = ApiTokenType.APP + resource_model = App + resource_id_field = "app_id" + token_prefix = "app-" + + @console_ns.response(200, "Agent service API keys", console_ns.models[ApiKeyList.__name__]) + @with_current_tenant_id + def get(self, tenant_id: str, agent_id: UUID) -> dict[str, object]: + app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) + return dump_response(ApiKeyList, self._get_api_key_list(str(app_model.id), tenant_id)) + + @console_ns.response(201, "Agent service API key created", console_ns.models[ApiKeyItem.__name__]) + @console_ns.response(400, "Maximum keys exceeded") + @with_current_tenant_id + @edit_permission_required + @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION) + def post(self, tenant_id: str, agent_id: UUID) -> tuple[dict[str, object], int]: + app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) + return dump_response(ApiKeyItem, self._create_api_key(str(app_model.id), tenant_id)), 201 + + +@console_ns.route("/agent//api-keys/") +class AgentApiKeyApi(BaseApiKeyResource): + resource_type = ApiTokenType.APP + resource_model = App + resource_id_field = "app_id" + + @console_ns.response(204, "Agent service API key deleted") + @with_current_user + @with_current_tenant_id + @rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_RELEASE_AND_VERSION) + def delete(self, tenant_id: str, current_user: Account, agent_id: UUID, api_key_id: UUID) -> tuple[str, int]: + app_model = _resolve_agent_app_model(tenant_id=tenant_id, agent_id=agent_id) + self._delete_api_key(str(app_model.id), str(api_key_id), tenant_id, current_user) + return "", 204 + + @console_ns.route("/agent/invite-options") class AgentInviteOptionsApi(Resource): @console_ns.doc(params=query_params_from_model(AgentInviteOptionsQuery)) diff --git a/api/controllers/service_api/app/app.py b/api/controllers/service_api/app/app.py index d670c7f5a6f..932ec71c769 100644 --- a/api/controllers/service_api/app/app.py +++ b/api/controllers/service_api/app/app.py @@ -2,6 +2,7 @@ from typing import Any, cast from flask_restx import Resource from pydantic import Field +from sqlalchemy import select from controllers.common.fields import Parameters from controllers.common.schema import register_response_schema_models @@ -9,7 +10,11 @@ from controllers.service_api import service_api_ns from controllers.service_api.app.error import AppUnavailableError from controllers.service_api.wraps import validate_app_token from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict +from core.app.apps.agent_app.app_variable_projection import agent_app_variables_to_user_input_form +from extensions.ext_database import db from fields.base import ResponseModel +from models.agent import Agent, AgentConfigSnapshot, AgentScope, AgentSource, AgentStatus +from models.agent_config_entities import AgentSoulConfig from models.model import App, AppMode from services.app_service import AppService @@ -29,6 +34,40 @@ class AppMetaResponse(ResponseModel): register_response_schema_models(service_api_ns, Parameters, AppMetaResponse, AppInfoResponse) +def _get_agent_app_feature_dict_and_user_input_form(app_model: App) -> tuple[dict[str, Any], list[dict[str, Any]]]: + app_model_config = app_model.app_model_config + features_dict = cast(dict[str, Any], app_model_config.to_dict()) if app_model_config is not None else {} + + agent = db.session.scalar( + select(Agent) + .where( + Agent.tenant_id == app_model.tenant_id, + Agent.app_id == app_model.id, + Agent.scope == AgentScope.ROSTER, + Agent.source == AgentSource.AGENT_APP, + Agent.status == AgentStatus.ACTIVE, + ) + .limit(1) + ) + if agent is None or not agent.active_config_snapshot_id: + raise AppUnavailableError() + + snapshot = db.session.scalar( + select(AgentConfigSnapshot) + .where( + AgentConfigSnapshot.tenant_id == app_model.tenant_id, + AgentConfigSnapshot.agent_id == agent.id, + AgentConfigSnapshot.id == agent.active_config_snapshot_id, + ) + .limit(1) + ) + if snapshot is None: + raise AppUnavailableError() + + agent_soul = AgentSoulConfig.model_validate(snapshot.config_snapshot_dict) + return features_dict, agent_app_variables_to_user_input_form(agent_soul.app_variables) + + @service_api_ns.route("/parameters") class AppParameterApi(Resource): """Resource for app variables.""" @@ -61,12 +100,16 @@ class AppParameterApi(Resource): Returns the input form parameters and configuration for the application. """ - if app_model.mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}: + features_dict: dict[str, Any] + user_input_form: list[dict[str, Any]] + if app_model.mode == AppMode.AGENT: + features_dict, user_input_form = _get_agent_app_feature_dict_and_user_input_form(app_model) + elif app_model.mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}: workflow = app_model.workflow if workflow is None: raise AppUnavailableError() - features_dict: dict[str, Any] = workflow.features_dict + features_dict = workflow.features_dict user_input_form = workflow.user_input_form(to_old_structure=True) else: app_model_config = app_model.app_model_config diff --git a/api/core/app/apps/agent_app/app_config_manager.py b/api/core/app/apps/agent_app/app_config_manager.py index 6721f8bcb1c..0dc04735cc0 100644 --- a/api/core/app/apps/agent_app/app_config_manager.py +++ b/api/core/app/apps/agent_app/app_config_manager.py @@ -21,6 +21,7 @@ from core.app.app_config.entities import ( EasyUIBasedAppModelConfigFrom, PromptTemplateEntity, ) +from core.app.apps.agent_app.app_variable_projection import agent_app_variables_to_user_input_form from models.agent_config_entities import AgentSoulConfig from models.model import App, AppMode, AppModelConfig, AppModelConfigDict, Conversation @@ -98,8 +99,7 @@ class AgentAppConfigManager(BaseAppConfigManager): # pipeline's bookkeeping (token counting, persistence). base["prompt_type"] = PromptTemplateEntity.PromptType.SIMPLE.value base["pre_prompt"] = agent_soul.prompt.system_prompt or "" - # Agent App takes the user message directly; no completion-style inputs form. - base.setdefault("user_input_form", []) + base["user_input_form"] = agent_app_variables_to_user_input_form(agent_soul.app_variables) return base diff --git a/api/core/app/apps/agent_app/app_variable_projection.py b/api/core/app/apps/agent_app/app_variable_projection.py new file mode 100644 index 00000000000..0feea12f5b8 --- /dev/null +++ b/api/core/app/apps/agent_app/app_variable_projection.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any + +from models.agent_config_entities import AppVariableConfig + + +def agent_app_variables_to_user_input_form(app_variables: Sequence[AppVariableConfig]) -> list[dict[str, Any]]: + """Project Agent Soul app variables into the legacy service-API parameter form.""" + + user_input_form: list[dict[str, Any]] = [] + for variable in app_variables: + form_type = _form_type_for_agent_variable(variable.type) + form_item: dict[str, Any] = { + "label": variable.name, + "variable": variable.name, + "required": variable.required, + } + if variable.default is not None: + form_item["default"] = variable.default + user_input_form.append({form_type: form_item}) + return user_input_form + + +def _form_type_for_agent_variable(variable_type: str) -> str: + normalized = variable_type.strip().lower() + if normalized in {"number", "integer", "float"}: + return "number" + if normalized in {"boolean", "bool"}: + return "checkbox" + if normalized in {"paragraph", "long_text", "multiline"}: + return "paragraph" + return "text-input" + + +__all__ = ["agent_app_variables_to_user_input_form"] diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index 6f507b889b6..ef11a817662 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -391,6 +391,80 @@ Check if activation token is valid | 400 | Invalid request parameters | | | 403 | Insufficient permissions | | +### [GET] /agent/{agent_id}/api-access +#### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ------ | +| agent_id | path | | Yes | string (uuid) | + +#### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 200 | Agent service API access | **application/json**: [AgentApiAccessResponse](#agentapiaccessresponse)
| + +### [POST] /agent/{agent_id}/api-enable +#### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ------ | +| agent_id | path | | Yes | string (uuid) | + +#### Request Body + +| Required | Schema | +| -------- | ------ | +| Yes | **application/json**: [AgentApiStatusPayload](#agentapistatuspayload)
| + +#### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 200 | Agent service API status updated | **application/json**: [AgentApiAccessResponse](#agentapiaccessresponse)
| +| 403 | Insufficient permissions | | + +### [GET] /agent/{agent_id}/api-keys +#### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ------ | +| agent_id | path | | Yes | string (uuid) | + +#### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 200 | Agent service API keys | **application/json**: [ApiKeyList](#apikeylist)
| + +### [POST] /agent/{agent_id}/api-keys +#### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ------ | +| agent_id | path | | Yes | string (uuid) | + +#### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 201 | Agent service API key created | **application/json**: [ApiKeyItem](#apikeyitem)
| +| 400 | Maximum keys exceeded | | + +### [DELETE] /agent/{agent_id}/api-keys/{api_key_id} +#### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ------ | +| agent_id | path | | Yes | string (uuid) | +| api_key_id | path | | Yes | string (uuid) | + +#### Responses + +| Code | Description | +| ---- | ----------- | +| 204 | Agent service API key deleted | + ### [GET] /agent/{agent_id}/chat-messages Get Agent App chat messages for a conversation with pagination @@ -12033,6 +12107,31 @@ Default namespace | chat_prompt_config | object | | No | | completion_prompt_config | object | | No | +#### AgentApiAccessResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| api_key_count | integer | | Yes | +| api_rph | integer | | Yes | +| api_rpm | integer | | Yes | +| chat_endpoint | string | | Yes | +| conversations_endpoint | string | | Yes | +| enabled | boolean | | Yes | +| files_upload_endpoint | string | | Yes | +| info_endpoint | string | | Yes | +| messages_endpoint | string | | Yes | +| meta_endpoint | string | | Yes | +| parameters_endpoint | string | | Yes | +| service_api_base_url | string | | Yes | +| stop_endpoint | string | | Yes | +| streaming_only | boolean,
**Default:** true | | No | + +#### AgentApiStatusPayload + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| enable_api | boolean | Enable or disable Agent service API | Yes | + #### AgentAppComposerResponse | Name | Type | Description | Required | diff --git a/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py b/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py index cf02dd3bcc5..65ab0014d1a 100644 --- a/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py +++ b/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py @@ -20,6 +20,10 @@ from controllers.console.agent.composer import ( WorkflowAgentComposerValidateApi, ) from controllers.console.agent.roster import ( + AgentApiAccessApi, + AgentApiKeyApi, + AgentApiKeyListApi, + AgentApiStatusApi, AgentAppApi, AgentAppCopyApi, AgentAppListApi, @@ -150,6 +154,10 @@ def test_agent_v2_console_routes_are_agent_id_first() -> None: "/agent//sandbox/files", "/agent//skills/upload", "/agent//files", + "/agent//api-access", + "/agent//api-enable", + "/agent//api-keys", + "/agent//api-keys/", "/agent//chat-messages", "/agent//chat-messages//stop", "/agent//feedbacks", @@ -177,6 +185,7 @@ def test_agent_v2_console_routes_are_agent_id_first() -> None: "/apps//agent-features", "/apps//agent-referencing-workflows", "/apps//agent-sandbox/files", + "/apps//api-access", ): assert route not in paths @@ -449,6 +458,127 @@ def test_agent_app_copy_uses_agent_id_and_returns_agent_detail( } +def test_agent_api_access_uses_agent_id_and_returns_service_api_metadata( + monkeypatch: pytest.MonkeyPatch, +) -> None: + agent_id = "00000000-0000-0000-0000-000000000001" + app_model = SimpleNamespace( + id="app-1", + enable_api=True, + api_base_url="https://api.example.test/v1", + api_rpm=60, + api_rph=600, + ) + monkeypatch.setattr(roster_controller, "_resolve_agent_app_model", lambda **kwargs: app_model) + monkeypatch.setattr(roster_controller, "_agent_api_key_count", lambda app_id: 2) + + response = unwrap(AgentApiAccessApi.get)(AgentApiAccessApi(), "tenant-1", agent_id) + + assert response == { + "enabled": True, + "service_api_base_url": "https://api.example.test/v1", + "streaming_only": True, + "chat_endpoint": "https://api.example.test/v1/chat-messages", + "stop_endpoint": "https://api.example.test/v1/chat-messages/{task_id}/stop", + "conversations_endpoint": "https://api.example.test/v1/conversations", + "messages_endpoint": "https://api.example.test/v1/messages", + "files_upload_endpoint": "https://api.example.test/v1/files/upload", + "parameters_endpoint": "https://api.example.test/v1/parameters", + "info_endpoint": "https://api.example.test/v1/info", + "meta_endpoint": "https://api.example.test/v1/meta", + "api_rpm": 60, + "api_rph": 600, + "api_key_count": 2, + } + + +def test_agent_api_status_and_key_routes_resolve_backing_app( + app: Flask, + monkeypatch: pytest.MonkeyPatch, +) -> None: + agent_id = "00000000-0000-0000-0000-000000000001" + api_key_id = "00000000-0000-0000-0000-000000000002" + app_model = SimpleNamespace( + id="app-1", + enable_api=False, + api_base_url="https://api.example.test/v1", + api_rpm=0, + api_rph=0, + ) + captured: dict[str, object] = {} + + monkeypatch.setattr(roster_controller, "_resolve_agent_app_model", lambda **kwargs: app_model) + monkeypatch.setattr(roster_controller, "_agent_api_key_count", lambda app_id: 1) + + class FakeAppService: + def update_app_api_status(self, app_obj: object, enable_api: bool) -> object: + captured["enable"] = {"app": app_obj, "enable_api": enable_api} + app_model.enable_api = enable_api + return app_model + + monkeypatch.setattr(roster_controller, "AppService", FakeAppService) + + def fake_get_api_key_list(self, resource_id: str, tenant_id: str): + captured["list_keys"] = {"resource_id": resource_id, "tenant_id": tenant_id} + return roster_controller.ApiKeyList(data=[]) + + def fake_create_api_key(self, resource_id: str, tenant_id: str): + captured["create_key"] = {"resource_id": resource_id, "tenant_id": tenant_id} + return SimpleNamespace( + id=api_key_id, + type="app", + token="app-test-token", + last_used_at=None, + created_at=None, + ) + + def fake_delete_api_key(self, resource_id: str, key_id: str, tenant_id: str, current_user: object) -> None: + captured["delete_key"] = { + "resource_id": resource_id, + "api_key_id": key_id, + "tenant_id": tenant_id, + "current_user": current_user, + } + + monkeypatch.setattr(AgentApiKeyListApi, "_get_api_key_list", fake_get_api_key_list) + monkeypatch.setattr(AgentApiKeyListApi, "_create_api_key", fake_create_api_key) + monkeypatch.setattr(AgentApiKeyApi, "_delete_api_key", fake_delete_api_key) + + with app.test_request_context( + "/console/api/agent/00000000-0000-0000-0000-000000000001/api-enable", + json={"enable_api": True}, + ): + enabled = unwrap(AgentApiStatusApi.post)(AgentApiStatusApi(), "tenant-1", agent_id) + assert enabled["enabled"] is True + assert captured["enable"] == {"app": app_model, "enable_api": True} + + keys = unwrap(AgentApiKeyListApi.get)(AgentApiKeyListApi(), "tenant-1", agent_id) + assert keys == {"data": []} + assert captured["list_keys"] == {"resource_id": "app-1", "tenant_id": "tenant-1"} + + created, status = unwrap(AgentApiKeyListApi.post)(AgentApiKeyListApi(), "tenant-1", agent_id) + assert status == 201 + assert created["id"] == api_key_id + assert created["token"] == "app-test-token" + assert captured["create_key"] == {"resource_id": "app-1", "tenant_id": "tenant-1"} + + current_user = SimpleNamespace(id="account-1", is_admin_or_owner=True) + deleted, delete_status = unwrap(AgentApiKeyApi.delete)( + AgentApiKeyApi(), + "tenant-1", + current_user, + agent_id, + api_key_id, + ) + assert (deleted, delete_status) == ("", 204) + assert captured["delete_key"] == { + "resource_id": "app-1", + "api_key_id": api_key_id, + "tenant_id": "tenant-1", + "current_user": current_user, + } + + def test_agent_app_update_rejects_empty_role(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: agent_id = "00000000-0000-0000-0000-000000000001" app_model = _app_detail_obj(id="app-1", bound_agent_id=agent_id) diff --git a/api/tests/unit_tests/controllers/service_api/app/test_app.py b/api/tests/unit_tests/controllers/service_api/app/test_app.py index 437bed9be48..9bc020b8b8f 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_app.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_app.py @@ -136,6 +136,55 @@ class TestAppParameterApi: assert "user_input_form" in response assert "opening_statement" in response + @patch("controllers.service_api.wraps.user_logged_in") + @patch("controllers.service_api.wraps.current_app") + @patch("controllers.service_api.wraps.validate_and_get_api_token") + @patch("controllers.service_api.wraps.db") + @patch("controllers.service_api.app.app._get_agent_app_feature_dict_and_user_input_form") + def test_get_parameters_for_agent_app( + self, + mock_get_agent_parameters, + mock_db, + mock_validate_token, + mock_current_app, + mock_user_logged_in, + app: Flask, + mock_app_model, + ): + """Test retrieving parameters for an Agent App from Agent Soul app variables.""" + _configure_current_app_mock(mock_current_app) + + mock_app_model.mode = AppMode.AGENT + mock_app_model.app_model_config = None + mock_app_model.workflow = None + mock_get_agent_parameters.return_value = ( + {"opening_statement": "Hi from Agent"}, + [{"text-input": {"label": "topic", "variable": "topic", "required": True}}], + ) + + mock_api_token = Mock() + mock_api_token.app_id = mock_app_model.id + mock_api_token.tenant_id = mock_app_model.tenant_id + mock_validate_token.return_value = mock_api_token + + mock_tenant = Mock() + mock_tenant.status = TenantStatus.NORMAL + mock_db.session.get.side_effect = [mock_app_model, mock_tenant] + + mock_account = Mock() + mock_account.current_tenant = mock_tenant + setup_mock_tenant_owner_execute_result(mock_db, mock_tenant, mock_account) + + with app.test_request_context("/parameters", method="GET", headers={"Authorization": "Bearer test_token"}): + api = AppParameterApi() + response = api.get() + + assert response["opening_statement"] == "Hi from Agent" + assert response["user_input_form"] == [ + {"text-input": {"label": "topic", "variable": "topic", "required": True}} + ] + mock_get_agent_parameters.assert_called_once_with(mock_app_model) + @patch("controllers.service_api.wraps.user_logged_in") @patch("controllers.service_api.wraps.current_app") @patch("controllers.service_api.wraps.validate_and_get_api_token") diff --git a/api/tests/unit_tests/core/app/apps/agent_app/test_app_config_manager.py b/api/tests/unit_tests/core/app/apps/agent_app/test_app_config_manager.py index 6edcaca5a64..c2a49d1b3a6 100644 --- a/api/tests/unit_tests/core/app/apps/agent_app/test_app_config_manager.py +++ b/api/tests/unit_tests/core/app/apps/agent_app/test_app_config_manager.py @@ -19,6 +19,10 @@ def _soul() -> AgentSoulConfig: "model_settings": {"temperature": 0.2}, }, "prompt": {"system_prompt": "You are Iris."}, + "app_variables": [ + {"name": "topic", "type": "string", "required": True}, + {"name": "count", "type": "number", "default": 3}, + ], } ) @@ -32,7 +36,10 @@ def test_model_and_prompt_come_from_soul(): "completion_params": {"temperature": 0.2}, } assert d["pre_prompt"] == "You are Iris." - assert d["user_input_form"] == [] + assert d["user_input_form"] == [ + {"text-input": {"label": "topic", "variable": "topic", "required": True}}, + {"number": {"label": "count", "variable": "count", "required": False, "default": 3}}, + ] def test_feature_flags_come_from_app_model_config_when_present(): diff --git a/packages/contracts/generated/api/console/agent/orpc.gen.ts b/packages/contracts/generated/api/console/agent/orpc.gen.ts index 3c9e9187cb2..99f44301703 100644 --- a/packages/contracts/generated/api/console/agent/orpc.gen.ts +++ b/packages/contracts/generated/api/console/agent/orpc.gen.ts @@ -4,6 +4,8 @@ import { oc } from '@orpc/contract' import * as z from 'zod' import { + zDeleteAgentByAgentIdApiKeysByApiKeyIdPath, + zDeleteAgentByAgentIdApiKeysByApiKeyIdResponse, zDeleteAgentByAgentIdFilesPath, zDeleteAgentByAgentIdFilesQuery, zDeleteAgentByAgentIdFilesResponse, @@ -11,6 +13,10 @@ import { zDeleteAgentByAgentIdResponse, zDeleteAgentByAgentIdSkillsBySlugPath, zDeleteAgentByAgentIdSkillsBySlugResponse, + zGetAgentByAgentIdApiAccessPath, + zGetAgentByAgentIdApiAccessResponse, + zGetAgentByAgentIdApiKeysPath, + zGetAgentByAgentIdApiKeysResponse, zGetAgentByAgentIdChatMessagesByMessageIdSuggestedQuestionsPath, zGetAgentByAgentIdChatMessagesByMessageIdSuggestedQuestionsResponse, zGetAgentByAgentIdChatMessagesPath, @@ -65,6 +71,11 @@ import { zGetAgentQuery, zGetAgentResponse, zPostAgentBody, + zPostAgentByAgentIdApiEnableBody, + zPostAgentByAgentIdApiEnablePath, + zPostAgentByAgentIdApiEnableResponse, + zPostAgentByAgentIdApiKeysPath, + zPostAgentByAgentIdApiKeysResponse, zPostAgentByAgentIdChatMessagesByTaskIdStopPath, zPostAgentByAgentIdChatMessagesByTaskIdStopResponse, zPostAgentByAgentIdComposerValidateBody, @@ -116,10 +127,87 @@ export const inviteOptions = { get, } +export const get2 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getAgentByAgentIdApiAccess', + path: '/agent/{agent_id}/api-access', + tags: ['console'], + }) + .input(z.object({ params: zGetAgentByAgentIdApiAccessPath })) + .output(zGetAgentByAgentIdApiAccessResponse) + +export const apiAccess = { + get: get2, +} + +export const post = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postAgentByAgentIdApiEnable', + path: '/agent/{agent_id}/api-enable', + tags: ['console'], + }) + .input( + z.object({ body: zPostAgentByAgentIdApiEnableBody, params: zPostAgentByAgentIdApiEnablePath }), + ) + .output(zPostAgentByAgentIdApiEnableResponse) + +export const apiEnable = { + post, +} + +export const delete_ = oc + .route({ + inputStructure: 'detailed', + method: 'DELETE', + operationId: 'deleteAgentByAgentIdApiKeysByApiKeyId', + path: '/agent/{agent_id}/api-keys/{api_key_id}', + successStatus: 204, + tags: ['console'], + }) + .input(z.object({ params: zDeleteAgentByAgentIdApiKeysByApiKeyIdPath })) + .output(zDeleteAgentByAgentIdApiKeysByApiKeyIdResponse) + +export const byApiKeyId = { + delete: delete_, +} + +export const get3 = oc + .route({ + inputStructure: 'detailed', + method: 'GET', + operationId: 'getAgentByAgentIdApiKeys', + path: '/agent/{agent_id}/api-keys', + tags: ['console'], + }) + .input(z.object({ params: zGetAgentByAgentIdApiKeysPath })) + .output(zGetAgentByAgentIdApiKeysResponse) + +export const post2 = oc + .route({ + inputStructure: 'detailed', + method: 'POST', + operationId: 'postAgentByAgentIdApiKeys', + path: '/agent/{agent_id}/api-keys', + successStatus: 201, + tags: ['console'], + }) + .input(z.object({ params: zPostAgentByAgentIdApiKeysPath })) + .output(zPostAgentByAgentIdApiKeysResponse) + +export const apiKeys = { + get: get3, + post: post2, + byApiKeyId, +} + /** * Get suggested questions for an Agent App message */ -export const get2 = oc +export const get4 = oc .route({ description: 'Get suggested questions for an Agent App message', inputStructure: 'detailed', @@ -132,7 +220,7 @@ export const get2 = oc .output(zGetAgentByAgentIdChatMessagesByMessageIdSuggestedQuestionsResponse) export const suggestedQuestions = { - get: get2, + get: get4, } export const byMessageId = { @@ -142,7 +230,7 @@ export const byMessageId = { /** * Stop a running Agent App chat message generation */ -export const post = oc +export const post3 = oc .route({ description: 'Stop a running Agent App chat message generation', inputStructure: 'detailed', @@ -155,7 +243,7 @@ export const post = oc .output(zPostAgentByAgentIdChatMessagesByTaskIdStopResponse) export const stop = { - post, + post: post3, } export const byTaskId = { @@ -165,7 +253,7 @@ export const byTaskId = { /** * Get Agent App chat messages for a conversation with pagination */ -export const get3 = oc +export const get5 = oc .route({ description: 'Get Agent App chat messages for a conversation with pagination', inputStructure: 'detailed', @@ -183,12 +271,12 @@ export const get3 = oc .output(zGetAgentByAgentIdChatMessagesResponse) export const chatMessages = { - get: get3, + get: get5, byMessageId, byTaskId, } -export const get4 = oc +export const get6 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -200,10 +288,10 @@ export const get4 = oc .output(zGetAgentByAgentIdComposerCandidatesResponse) export const candidates = { - get: get4, + get: get6, } -export const post2 = oc +export const post4 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -220,10 +308,10 @@ export const post2 = oc .output(zPostAgentByAgentIdComposerValidateResponse) export const validate = { - post: post2, + post: post4, } -export const get5 = oc +export const get7 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -246,13 +334,13 @@ export const put = oc .output(zPutAgentByAgentIdComposerResponse) export const composer = { - get: get5, + get: get7, put, candidates, validate, } -export const post3 = oc +export const post5 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -265,13 +353,13 @@ export const post3 = oc .output(zPostAgentByAgentIdCopyResponse) export const copy = { - post: post3, + post: post5, } /** * Time-limited external signed URL for one Agent App drive value */ -export const get6 = oc +export const get8 = oc .route({ description: 'Time-limited external signed URL for one Agent App drive value', inputStructure: 'detailed', @@ -289,13 +377,13 @@ export const get6 = oc .output(zGetAgentByAgentIdDriveFilesDownloadResponse) export const download = { - get: get6, + get: get8, } /** * Truncated text preview of one Agent App drive value */ -export const get7 = oc +export const get9 = oc .route({ description: 'Truncated text preview of one Agent App drive value', inputStructure: 'detailed', @@ -313,13 +401,13 @@ export const get7 = oc .output(zGetAgentByAgentIdDriveFilesPreviewResponse) export const preview = { - get: get7, + get: get9, } /** * List agent drive entries for an Agent App */ -export const get8 = oc +export const get10 = oc .route({ description: 'List agent drive entries for an Agent App', inputStructure: 'detailed', @@ -337,7 +425,7 @@ export const get8 = oc .output(zGetAgentByAgentIdDriveFilesResponse) export const files = { - get: get8, + get: get10, download, preview, } @@ -345,7 +433,7 @@ export const files = { /** * Inspect one drive-backed skill for slash-menu hover/detail UI */ -export const get9 = oc +export const get11 = oc .route({ description: 'Inspect one drive-backed skill for slash-menu hover/detail UI', inputStructure: 'detailed', @@ -358,7 +446,7 @@ export const get9 = oc .output(zGetAgentByAgentIdDriveSkillsBySkillPathInspectResponse) export const inspect = { - get: get9, + get: get11, } export const bySkillPath = { @@ -368,7 +456,7 @@ export const bySkillPath = { /** * List drive-backed skills for an Agent App */ -export const get10 = oc +export const get12 = oc .route({ description: 'List drive-backed skills for an Agent App', inputStructure: 'detailed', @@ -381,7 +469,7 @@ export const get10 = oc .output(zGetAgentByAgentIdDriveSkillsResponse) export const skills = { - get: get10, + get: get12, bySkillPath, } @@ -393,7 +481,7 @@ export const drive = { /** * Update an Agent App's presentation features (opener, follow-up, citations, ...) */ -export const post4 = oc +export const post6 = oc .route({ description: 'Update an Agent App\'s presentation features (opener, follow-up, citations, ...)', inputStructure: 'detailed', @@ -408,13 +496,13 @@ export const post4 = oc .output(zPostAgentByAgentIdFeaturesResponse) export const features = { - post: post4, + post: post6, } /** * Create or update Agent App message feedback */ -export const post5 = oc +export const post7 = oc .route({ description: 'Create or update Agent App message feedback', inputStructure: 'detailed', @@ -429,13 +517,13 @@ export const post5 = oc .output(zPostAgentByAgentIdFeedbacksResponse) export const feedbacks = { - post: post5, + post: post7, } /** * Delete one Agent App drive file by key */ -export const delete_ = oc +export const delete2 = oc .route({ description: 'Delete one Agent App drive file by key', inputStructure: 'detailed', @@ -452,7 +540,7 @@ export const delete_ = oc /** * Commit an uploaded file into the Agent App drive under files/ */ -export const post6 = oc +export const post8 = oc .route({ description: 'Commit an uploaded file into the Agent App drive under files/', inputStructure: 'detailed', @@ -466,11 +554,11 @@ export const post6 = oc .output(zPostAgentByAgentIdFilesResponse) export const files2 = { - delete: delete_, - post: post6, + delete: delete2, + post: post8, } -export const get11 = oc +export const get13 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -482,10 +570,10 @@ export const get11 = oc .output(zGetAgentByAgentIdLogSourcesResponse) export const logSources = { - get: get11, + get: get13, } -export const get12 = oc +export const get14 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -502,14 +590,14 @@ export const get12 = oc .output(zGetAgentByAgentIdLogsByConversationIdMessagesResponse) export const messages = { - get: get12, + get: get14, } export const byConversationId = { messages, } -export const get13 = oc +export const get15 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -523,14 +611,14 @@ export const get13 = oc .output(zGetAgentByAgentIdLogsResponse) export const logs = { - get: get13, + get: get15, byConversationId, } /** * Get Agent App message details by ID */ -export const get14 = oc +export const get16 = oc .route({ description: 'Get Agent App message details by ID', inputStructure: 'detailed', @@ -543,7 +631,7 @@ export const get14 = oc .output(zGetAgentByAgentIdMessagesByMessageIdResponse) export const byMessageId2 = { - get: get14, + get: get16, } export const messages2 = { @@ -553,7 +641,7 @@ export const messages2 = { /** * List workflow apps that reference this Agent App's bound Agent (read-only) */ -export const get15 = oc +export const get17 = oc .route({ description: 'List workflow apps that reference this Agent App\'s bound Agent (read-only)', inputStructure: 'detailed', @@ -566,13 +654,13 @@ export const get15 = oc .output(zGetAgentByAgentIdReferencingWorkflowsResponse) export const referencingWorkflows = { - get: get15, + get: get17, } /** * Read a text/binary preview file in an Agent App conversation sandbox */ -export const get16 = oc +export const get18 = oc .route({ description: 'Read a text/binary preview file in an Agent App conversation sandbox', inputStructure: 'detailed', @@ -590,13 +678,13 @@ export const get16 = oc .output(zGetAgentByAgentIdSandboxFilesReadResponse) export const read = { - get: get16, + get: get18, } /** * Upload one Agent App sandbox file as a Dify ToolFile mapping */ -export const post7 = oc +export const post9 = oc .route({ description: 'Upload one Agent App sandbox file as a Dify ToolFile mapping', inputStructure: 'detailed', @@ -614,13 +702,13 @@ export const post7 = oc .output(zPostAgentByAgentIdSandboxFilesUploadResponse) export const upload = { - post: post7, + post: post9, } /** * List a directory in an Agent App conversation sandbox */ -export const get17 = oc +export const get19 = oc .route({ description: 'List a directory in an Agent App conversation sandbox', inputStructure: 'detailed', @@ -638,7 +726,7 @@ export const get17 = oc .output(zGetAgentByAgentIdSandboxFilesResponse) export const files3 = { - get: get17, + get: get19, read, upload, } @@ -650,7 +738,7 @@ export const sandbox = { /** * Upload + standardize a Skill into an Agent App drive */ -export const post8 = oc +export const post10 = oc .route({ description: 'Upload + standardize a Skill into an Agent App drive', inputStructure: 'detailed', @@ -669,13 +757,13 @@ export const post8 = oc .output(zPostAgentByAgentIdSkillsUploadResponse) export const upload2 = { - post: post8, + post: post10, } /** * Infer CLI tool + ENV suggestions from a standardized Agent App skill */ -export const post9 = oc +export const post11 = oc .route({ description: 'Infer CLI tool + ENV suggestions from a standardized Agent App skill', inputStructure: 'detailed', @@ -688,13 +776,13 @@ export const post9 = oc .output(zPostAgentByAgentIdSkillsBySlugInferToolsResponse) export const inferTools = { - post: post9, + post: post11, } /** * Delete a standardized skill from an Agent App drive */ -export const delete2 = oc +export const delete3 = oc .route({ description: 'Delete a standardized skill from an Agent App drive', inputStructure: 'detailed', @@ -707,7 +795,7 @@ export const delete2 = oc .output(zDeleteAgentByAgentIdSkillsBySlugResponse) export const bySlug = { - delete: delete2, + delete: delete3, inferTools, } @@ -716,7 +804,7 @@ export const skills2 = { bySlug, } -export const get18 = oc +export const get20 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -733,14 +821,14 @@ export const get18 = oc .output(zGetAgentByAgentIdStatisticsSummaryResponse) export const summary = { - get: get18, + get: get20, } export const statistics = { summary, } -export const post10 = oc +export const post12 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -752,10 +840,10 @@ export const post10 = oc .output(zPostAgentByAgentIdVersionsByVersionIdRestoreResponse) export const restore = { - post: post10, + post: post12, } -export const get19 = oc +export const get21 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -767,11 +855,11 @@ export const get19 = oc .output(zGetAgentByAgentIdVersionsByVersionIdResponse) export const byVersionId = { - get: get19, + get: get21, restore, } -export const get20 = oc +export const get22 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -783,11 +871,11 @@ export const get20 = oc .output(zGetAgentByAgentIdVersionsResponse) export const versions = { - get: get20, + get: get22, byVersionId, } -export const delete3 = oc +export const delete4 = oc .route({ inputStructure: 'detailed', method: 'DELETE', @@ -799,7 +887,7 @@ export const delete3 = oc .input(z.object({ params: zDeleteAgentByAgentIdPath })) .output(zDeleteAgentByAgentIdResponse) -export const get21 = oc +export const get23 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -822,9 +910,12 @@ export const put2 = oc .output(zPutAgentByAgentIdResponse) export const byAgentId = { - delete: delete3, - get: get21, + delete: delete4, + get: get23, put: put2, + apiAccess, + apiEnable, + apiKeys, chatMessages, composer, copy, @@ -842,7 +933,7 @@ export const byAgentId = { versions, } -export const get22 = oc +export const get24 = oc .route({ inputStructure: 'detailed', method: 'GET', @@ -853,7 +944,7 @@ export const get22 = oc .input(z.object({ query: zGetAgentQuery.optional() })) .output(zGetAgentResponse) -export const post11 = oc +export const post13 = oc .route({ inputStructure: 'detailed', method: 'POST', @@ -866,8 +957,8 @@ export const post11 = oc .output(zPostAgentResponse) export const agent = { - get: get22, - post: post11, + get: get24, + post: post13, inviteOptions, byAgentId, } diff --git a/packages/contracts/generated/api/console/agent/types.gen.ts b/packages/contracts/generated/api/console/agent/types.gen.ts index 74acc33ed43..837b267a636 100644 --- a/packages/contracts/generated/api/console/agent/types.gen.ts +++ b/packages/contracts/generated/api/console/agent/types.gen.ts @@ -74,6 +74,39 @@ export type AgentAppUpdatePayload = { use_icon_as_answer_icon?: boolean | null } +export type AgentApiAccessResponse = { + api_key_count: number + api_rph: number + api_rpm: number + chat_endpoint: string + conversations_endpoint: string + enabled: boolean + files_upload_endpoint: string + info_endpoint: string + messages_endpoint: string + meta_endpoint: string + parameters_endpoint: string + service_api_base_url: string + stop_endpoint: string + streaming_only?: boolean +} + +export type AgentApiStatusPayload = { + enable_api: boolean +} + +export type ApiKeyList = { + data: Array +} + +export type ApiKeyItem = { + created_at?: number | null + id: string + last_used_at?: number | null + token: string + type: string +} + export type MessageInfiniteScrollPaginationResponse = { data: Array has_more: boolean @@ -1699,6 +1732,95 @@ export type PutAgentByAgentIdResponses = { export type PutAgentByAgentIdResponse = PutAgentByAgentIdResponses[keyof PutAgentByAgentIdResponses] +export type GetAgentByAgentIdApiAccessData = { + body?: never + path: { + agent_id: string + } + query?: never + url: '/agent/{agent_id}/api-access' +} + +export type GetAgentByAgentIdApiAccessResponses = { + 200: AgentApiAccessResponse +} + +export type GetAgentByAgentIdApiAccessResponse + = GetAgentByAgentIdApiAccessResponses[keyof GetAgentByAgentIdApiAccessResponses] + +export type PostAgentByAgentIdApiEnableData = { + body: AgentApiStatusPayload + path: { + agent_id: string + } + query?: never + url: '/agent/{agent_id}/api-enable' +} + +export type PostAgentByAgentIdApiEnableErrors = { + 403: unknown +} + +export type PostAgentByAgentIdApiEnableResponses = { + 200: AgentApiAccessResponse +} + +export type PostAgentByAgentIdApiEnableResponse + = PostAgentByAgentIdApiEnableResponses[keyof PostAgentByAgentIdApiEnableResponses] + +export type GetAgentByAgentIdApiKeysData = { + body?: never + path: { + agent_id: string + } + query?: never + url: '/agent/{agent_id}/api-keys' +} + +export type GetAgentByAgentIdApiKeysResponses = { + 200: ApiKeyList +} + +export type GetAgentByAgentIdApiKeysResponse + = GetAgentByAgentIdApiKeysResponses[keyof GetAgentByAgentIdApiKeysResponses] + +export type PostAgentByAgentIdApiKeysData = { + body?: never + path: { + agent_id: string + } + query?: never + url: '/agent/{agent_id}/api-keys' +} + +export type PostAgentByAgentIdApiKeysErrors = { + 400: unknown +} + +export type PostAgentByAgentIdApiKeysResponses = { + 201: ApiKeyItem +} + +export type PostAgentByAgentIdApiKeysResponse + = PostAgentByAgentIdApiKeysResponses[keyof PostAgentByAgentIdApiKeysResponses] + +export type DeleteAgentByAgentIdApiKeysByApiKeyIdData = { + body?: never + path: { + agent_id: string + api_key_id: string + } + query?: never + url: '/agent/{agent_id}/api-keys/{api_key_id}' +} + +export type DeleteAgentByAgentIdApiKeysByApiKeyIdResponses = { + 204: void +} + +export type DeleteAgentByAgentIdApiKeysByApiKeyIdResponse + = DeleteAgentByAgentIdApiKeysByApiKeyIdResponses[keyof DeleteAgentByAgentIdApiKeysByApiKeyIdResponses] + export type GetAgentByAgentIdChatMessagesData = { body?: never path: { diff --git a/packages/contracts/generated/api/console/agent/zod.gen.ts b/packages/contracts/generated/api/console/agent/zod.gen.ts index b35427c8bd0..297055a155c 100644 --- a/packages/contracts/generated/api/console/agent/zod.gen.ts +++ b/packages/contracts/generated/api/console/agent/zod.gen.ts @@ -2,6 +2,51 @@ import * as z from 'zod' +/** + * AgentApiAccessResponse + */ +export const zAgentApiAccessResponse = z.object({ + api_key_count: z.int(), + api_rph: z.int(), + api_rpm: z.int(), + chat_endpoint: z.string(), + conversations_endpoint: z.string(), + enabled: z.boolean(), + files_upload_endpoint: z.string(), + info_endpoint: z.string(), + messages_endpoint: z.string(), + meta_endpoint: z.string(), + parameters_endpoint: z.string(), + service_api_base_url: z.string(), + stop_endpoint: z.string(), + streaming_only: z.boolean().optional().default(true), +}) + +/** + * AgentApiStatusPayload + */ +export const zAgentApiStatusPayload = z.object({ + enable_api: z.boolean(), +}) + +/** + * ApiKeyItem + */ +export const zApiKeyItem = z.object({ + created_at: z.int().nullish(), + id: z.string(), + last_used_at: z.int().nullish(), + token: z.string(), + type: z.string(), +}) + +/** + * ApiKeyList + */ +export const zApiKeyList = z.object({ + data: z.array(zApiKeyItem), +}) + /** * SuggestedQuestionsResponse */ @@ -2257,6 +2302,54 @@ export const zPutAgentByAgentIdPath = z.object({ */ export const zPutAgentByAgentIdResponse = zAgentAppDetailWithSite +export const zGetAgentByAgentIdApiAccessPath = z.object({ + agent_id: z.uuid(), +}) + +/** + * Agent service API access + */ +export const zGetAgentByAgentIdApiAccessResponse = zAgentApiAccessResponse + +export const zPostAgentByAgentIdApiEnableBody = zAgentApiStatusPayload + +export const zPostAgentByAgentIdApiEnablePath = z.object({ + agent_id: z.uuid(), +}) + +/** + * Agent service API status updated + */ +export const zPostAgentByAgentIdApiEnableResponse = zAgentApiAccessResponse + +export const zGetAgentByAgentIdApiKeysPath = z.object({ + agent_id: z.uuid(), +}) + +/** + * Agent service API keys + */ +export const zGetAgentByAgentIdApiKeysResponse = zApiKeyList + +export const zPostAgentByAgentIdApiKeysPath = z.object({ + agent_id: z.uuid(), +}) + +/** + * Agent service API key created + */ +export const zPostAgentByAgentIdApiKeysResponse = zApiKeyItem + +export const zDeleteAgentByAgentIdApiKeysByApiKeyIdPath = z.object({ + agent_id: z.uuid(), + api_key_id: z.uuid(), +}) + +/** + * Agent service API key deleted + */ +export const zDeleteAgentByAgentIdApiKeysByApiKeyIdResponse = z.void() + export const zGetAgentByAgentIdChatMessagesPath = z.object({ agent_id: z.uuid(), })