mirror of
https://github.com/langgenius/dify.git
synced 2026-06-24 13:01:16 +08:00
feat(agent): add roster service api access (#37759)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
e3d0320826
commit
7d2f25df8e
@ -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/<uuid:agent_id>/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/<uuid:agent_id>/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/<uuid:agent_id>/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/<uuid:agent_id>/api-keys/<uuid:api_key_id>")
|
||||
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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
37
api/core/app/apps/agent_app/app_variable_projection.py
Normal file
37
api/core/app/apps/agent_app/app_variable_projection.py
Normal file
@ -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"]
|
||||
@ -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)<br> |
|
||||
|
||||
### [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)<br> |
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Agent service API status updated | **application/json**: [AgentApiAccessResponse](#agentapiaccessresponse)<br> |
|
||||
| 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)<br> |
|
||||
|
||||
### [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)<br> |
|
||||
| 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, <br>**Default:** true | | No |
|
||||
|
||||
#### AgentApiStatusPayload
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| enable_api | boolean | Enable or disable Agent service API | Yes |
|
||||
|
||||
#### AgentAppComposerResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|
||||
@ -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/<uuid:agent_id>/sandbox/files",
|
||||
"/agent/<uuid:agent_id>/skills/upload",
|
||||
"/agent/<uuid:agent_id>/files",
|
||||
"/agent/<uuid:agent_id>/api-access",
|
||||
"/agent/<uuid:agent_id>/api-enable",
|
||||
"/agent/<uuid:agent_id>/api-keys",
|
||||
"/agent/<uuid:agent_id>/api-keys/<uuid:api_key_id>",
|
||||
"/agent/<uuid:agent_id>/chat-messages",
|
||||
"/agent/<uuid:agent_id>/chat-messages/<string:task_id>/stop",
|
||||
"/agent/<uuid:agent_id>/feedbacks",
|
||||
@ -177,6 +185,7 @@ def test_agent_v2_console_routes_are_agent_id_first() -> None:
|
||||
"/apps/<uuid:app_id>/agent-features",
|
||||
"/apps/<uuid:app_id>/agent-referencing-workflows",
|
||||
"/apps/<uuid:app_id>/agent-sandbox/files",
|
||||
"/apps/<uuid:agent_id>/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)
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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/<name>
|
||||
*/
|
||||
export const post6 = oc
|
||||
export const post8 = oc
|
||||
.route({
|
||||
description: 'Commit an uploaded file into the Agent App drive under files/<name>',
|
||||
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,
|
||||
}
|
||||
|
||||
@ -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<ApiKeyItem>
|
||||
}
|
||||
|
||||
export type ApiKeyItem = {
|
||||
created_at?: number | null
|
||||
id: string
|
||||
last_used_at?: number | null
|
||||
token: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export type MessageInfiniteScrollPaginationResponse = {
|
||||
data: Array<MessageDetailResponse>
|
||||
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: {
|
||||
|
||||
@ -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(),
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user