mirror of
https://github.com/langgenius/dify.git
synced 2026-06-26 06:41:10 +08:00
Merge ffe3a5729e into a246dc8b17
This commit is contained in:
commit
79e646d0ed
@ -167,12 +167,16 @@ register_schema_models(
|
||||
ChatMessagesQuery,
|
||||
MessageFeedbackPayload,
|
||||
FeedbackExportQuery,
|
||||
)
|
||||
register_response_schema_models(
|
||||
console_ns,
|
||||
AnnotationCountResponse,
|
||||
SuggestedQuestionsResponse,
|
||||
MessageDetailResponse,
|
||||
MessageInfiniteScrollPaginationResponse,
|
||||
SimpleResultResponse,
|
||||
TextFileResponse,
|
||||
)
|
||||
register_response_schema_models(console_ns, SimpleResultResponse, TextFileResponse)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/chat-messages")
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import logging
|
||||
|
||||
from flask import request
|
||||
from flask_restx import fields, marshal_with
|
||||
from pydantic import field_validator
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import services
|
||||
from controllers.common.controller_schemas import TextToAudioPayload as TextToAudioPayloadBase
|
||||
from controllers.common.fields import AudioBinaryResponse, AudioTranscriptResponse
|
||||
from controllers.web import web_ns
|
||||
from controllers.web.error import (
|
||||
AppUnavailableError,
|
||||
@ -23,8 +21,9 @@ from controllers.web.error import (
|
||||
from controllers.web.wraps import WebApiResource
|
||||
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
|
||||
from extensions.ext_database import db
|
||||
from fields.base import ResponseModel
|
||||
from graphon.model_runtime.errors.invoke import InvokeError
|
||||
from libs.helper import uuid_value
|
||||
from libs.helper import dump_response, uuid_value
|
||||
from models.model import App, EndUser
|
||||
from services.audio_service import AudioService
|
||||
from services.errors.audio import (
|
||||
@ -37,6 +36,10 @@ from services.errors.audio import (
|
||||
from ..common.schema import register_response_schema_models, register_schema_models
|
||||
|
||||
|
||||
class AudioToTextResponse(ResponseModel):
|
||||
text: str
|
||||
|
||||
|
||||
class TextToAudioPayload(TextToAudioPayloadBase):
|
||||
@field_validator("message_id")
|
||||
@classmethod
|
||||
@ -47,18 +50,13 @@ class TextToAudioPayload(TextToAudioPayloadBase):
|
||||
|
||||
|
||||
register_schema_models(web_ns, TextToAudioPayload)
|
||||
register_response_schema_models(web_ns, AudioBinaryResponse, AudioTranscriptResponse)
|
||||
register_response_schema_models(web_ns, AudioToTextResponse)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@web_ns.route("/audio-to-text")
|
||||
class AudioApi(WebApiResource):
|
||||
audio_to_text_response_fields = {
|
||||
"text": fields.String,
|
||||
}
|
||||
|
||||
@marshal_with(audio_to_text_response_fields)
|
||||
@web_ns.doc("Audio to Text")
|
||||
@web_ns.doc(description="Convert audio file to text using speech-to-text service.")
|
||||
@web_ns.doc(
|
||||
@ -72,7 +70,7 @@ class AudioApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[AudioTranscriptResponse.__name__])
|
||||
@web_ns.response(200, "Success", web_ns.models[AudioToTextResponse.__name__])
|
||||
def post(self, app_model: App, end_user: EndUser):
|
||||
"""Convert audio to text"""
|
||||
file = request.files["file"]
|
||||
@ -80,7 +78,7 @@ class AudioApi(WebApiResource):
|
||||
try:
|
||||
response = AudioService.transcript_asr(app_model=app_model, file=file, end_user=end_user.external_user_id)
|
||||
|
||||
return response
|
||||
return dump_response(AudioToTextResponse, response)
|
||||
except services.errors.app_model_config.AppModelConfigBrokenError:
|
||||
logger.exception("App model config broken.")
|
||||
raise AppUnavailableError()
|
||||
@ -121,7 +119,8 @@ class TextApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[AudioBinaryResponse.__name__])
|
||||
# response-contract:ignore provider audio bytes; TODO: model binary audio response if shape is standardized.
|
||||
@web_ns.response(200, "Success")
|
||||
def post(self, app_model: App, end_user: EndUser):
|
||||
"""Convert text to audio"""
|
||||
try:
|
||||
@ -130,7 +129,7 @@ class TextApi(WebApiResource):
|
||||
message_id = payload.message_id
|
||||
text = payload.text
|
||||
voice = payload.voice
|
||||
response = AudioService.transcript_tts(
|
||||
return AudioService.transcript_tts(
|
||||
app_model=app_model,
|
||||
session=db.session,
|
||||
text=text,
|
||||
@ -138,8 +137,6 @@ class TextApi(WebApiResource):
|
||||
end_user=end_user.external_user_id,
|
||||
message_id=message_id,
|
||||
)
|
||||
|
||||
return response
|
||||
except services.errors.app_model_config.AppModelConfigBrokenError:
|
||||
logger.exception("App model config broken.")
|
||||
raise AppUnavailableError()
|
||||
|
||||
@ -5,7 +5,7 @@ from pydantic import BaseModel, Field, field_validator
|
||||
from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
|
||||
|
||||
import services
|
||||
from controllers.common.fields import GeneratedAppResponse, SimpleResultResponse
|
||||
from controllers.common.fields import SimpleResultResponse
|
||||
from controllers.common.schema import register_response_schema_models, register_schema_models
|
||||
from controllers.web import web_ns
|
||||
from controllers.web.error import (
|
||||
@ -87,7 +87,7 @@ class ChatMessagePayload(BaseModel):
|
||||
|
||||
|
||||
register_schema_models(web_ns, CompletionMessagePayload, ChatMessagePayload)
|
||||
register_response_schema_models(web_ns, GeneratedAppResponse, SimpleResultResponse)
|
||||
register_response_schema_models(web_ns, SimpleResultResponse)
|
||||
|
||||
|
||||
# define completion api for user
|
||||
@ -106,7 +106,7 @@ class CompletionApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[GeneratedAppResponse.__name__])
|
||||
@web_ns.response(200, "Success")
|
||||
def post(self, app_model: App, end_user: EndUser):
|
||||
if app_model.mode != AppMode.COMPLETION:
|
||||
raise NotCompletionAppError()
|
||||
@ -122,6 +122,7 @@ class CompletionApi(WebApiResource):
|
||||
app_model=app_model, user=end_user, args=args, invoke_from=InvokeFrom.WEB_APP, streaming=streaming
|
||||
)
|
||||
|
||||
# response-contract:ignore compact_generate_response
|
||||
return helper.compact_generate_response(response)
|
||||
except services.errors.conversation.ConversationNotExistsError:
|
||||
raise NotFound("Conversation Not Exists.")
|
||||
@ -172,7 +173,7 @@ class CompletionStopApi(WebApiResource):
|
||||
app_mode=AppMode.value_of(app_model.mode),
|
||||
)
|
||||
|
||||
return {"result": "success"}, 200
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@web_ns.route("/chat-messages")
|
||||
@ -190,7 +191,7 @@ class ChatApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[GeneratedAppResponse.__name__])
|
||||
@web_ns.response(200, "Success")
|
||||
def post(self, app_model: App, end_user: EndUser):
|
||||
app_mode = AppMode.value_of(app_model.mode)
|
||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}:
|
||||
@ -213,6 +214,7 @@ class ChatApi(WebApiResource):
|
||||
app_model=app_model, user=end_user, args=args, invoke_from=InvokeFrom.WEB_APP, streaming=streaming
|
||||
)
|
||||
|
||||
# response-contract:ignore compact_generate_response
|
||||
return helper.compact_generate_response(response)
|
||||
except services.errors.conversation.ConversationNotExistsError:
|
||||
raise NotFound("Conversation Not Exists.")
|
||||
@ -266,4 +268,4 @@ class ChatStopApi(WebApiResource):
|
||||
app_mode=app_mode,
|
||||
)
|
||||
|
||||
return {"result": "success"}, 200
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||
|
||||
@ -13,6 +13,7 @@ from controllers.web import web_ns
|
||||
from controllers.web.wraps import WebApiResource
|
||||
from extensions.ext_database import db
|
||||
from fields.file_fields import FileResponse
|
||||
from libs.helper import dump_response
|
||||
from models.model import App, EndUser
|
||||
from services.file_service import FileService
|
||||
|
||||
@ -84,5 +85,4 @@ class FileApi(WebApiResource):
|
||||
except services.errors.file.UnsupportedFileTypeError:
|
||||
raise UnsupportedFileTypeError()
|
||||
|
||||
response = FileResponse.model_validate(upload_file, from_attributes=True)
|
||||
return response.model_dump(mode="json"), 201
|
||||
return dump_response(FileResponse, upload_file), 201
|
||||
|
||||
@ -29,6 +29,7 @@ from extensions.ext_database import db
|
||||
from fields.file_fields import FileResponse, FileWithSignedUrl
|
||||
from graphon.file import helpers as file_helpers
|
||||
from libs.exception import BaseHTTPException
|
||||
from libs.helper import dump_response
|
||||
from repositories.factory import DifyAPIRepositoryFactory
|
||||
from services.file_service import FileService
|
||||
from services.human_input_file_upload_service import (
|
||||
@ -141,8 +142,7 @@ def _upload_local_file(context):
|
||||
except services.errors.file.BlockedFileExtensionError as exc:
|
||||
raise BlockedFileExtensionError() from exc
|
||||
|
||||
response = FileResponse.model_validate(upload_file, from_attributes=True)
|
||||
return upload_file.id, response
|
||||
return upload_file.id, dump_response(FileResponse, upload_file)
|
||||
|
||||
|
||||
def _upload_remote_file(context, url: str):
|
||||
@ -186,7 +186,7 @@ def _upload_remote_file(context, url: str):
|
||||
created_by=upload_file.created_by,
|
||||
created_at=int(upload_file.created_at.timestamp()),
|
||||
)
|
||||
return upload_file.id, response
|
||||
return upload_file.id, response.model_dump(mode="json")
|
||||
|
||||
|
||||
@web_ns.route("/human-input-forms/files")
|
||||
@ -209,4 +209,5 @@ class HumanInputFileUploadApi(Resource):
|
||||
file_id, response = _upload_local_file(context=context)
|
||||
|
||||
upload_service.record_upload_file(context=context, file_id=file_id)
|
||||
return response.model_dump(mode="json"), 201
|
||||
# response-contract:ignore pre-dumped response. See above
|
||||
return response, 201
|
||||
|
||||
@ -2,14 +2,12 @@
|
||||
Web App Human Input Form APIs.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, NotRequired, TypedDict
|
||||
from typing import Self
|
||||
|
||||
from flask import Response, request
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from werkzeug.exceptions import Forbidden
|
||||
@ -20,35 +18,58 @@ from controllers.common.human_input import HumanInputFormSubmitPayload, stringif
|
||||
from controllers.common.schema import register_response_schema_models, register_schema_models
|
||||
from controllers.web import web_ns
|
||||
from controllers.web.error import WebFormRateLimitExceededError
|
||||
from controllers.web.site import serialize_app_site_payload
|
||||
from controllers.web.site import WebAppSiteResponse
|
||||
from extensions.ext_database import db
|
||||
from graphon.nodes.human_input.entities import FormInputConfig
|
||||
from libs.helper import RateLimiter, extract_remote_ip, to_timestamp
|
||||
from fields.base import ResponseModel
|
||||
from graphon.nodes.human_input.entities import FormInputConfig, UserActionConfig
|
||||
from libs.helper import RateLimiter, dump_response, extract_remote_ip, to_timestamp
|
||||
from models.account import TenantStatus
|
||||
from models.model import App, Site
|
||||
from repositories.factory import DifyAPIRepositoryFactory
|
||||
from services.feature_service import FeatureService
|
||||
from services.human_input_file_upload_service import HumanInputFileUploadService
|
||||
from services.human_input_service import Form, FormNotFoundError, HumanInputService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HumanInputUploadTokenResponse(BaseModel):
|
||||
class HumanInputUploadTokenResponse(ResponseModel):
|
||||
upload_token: str
|
||||
expires_at: int
|
||||
|
||||
|
||||
class HumanInputFormDefinitionResponse(BaseModel):
|
||||
form_content: Any
|
||||
inputs: Any
|
||||
class HumanInputFormDefinitionResponse(ResponseModel):
|
||||
form_content: str
|
||||
inputs: list[FormInputConfig]
|
||||
resolved_default_values: dict[str, str]
|
||||
user_actions: Any
|
||||
user_actions: list[UserActionConfig]
|
||||
expiration_time: int
|
||||
site: dict[str, Any] | None = Field(default=None)
|
||||
site: WebAppSiteResponse | None = None
|
||||
|
||||
@classmethod
|
||||
def from_form(
|
||||
cls,
|
||||
form: Form,
|
||||
*,
|
||||
inputs: Sequence[FormInputConfig] = (),
|
||||
site: WebAppSiteResponse | None = None,
|
||||
) -> Self:
|
||||
definition_payload = form.get_definition().model_dump(mode="json")
|
||||
expiration_time = to_timestamp(form.expiration_time)
|
||||
if expiration_time is None:
|
||||
raise ValueError("Human input form expiration_time is required")
|
||||
return cls(
|
||||
form_content=definition_payload["rendered_content"],
|
||||
inputs=list(inputs),
|
||||
resolved_default_values=stringify_form_default_values(definition_payload["default_values"]),
|
||||
user_actions=definition_payload["user_actions"],
|
||||
expiration_time=expiration_time,
|
||||
site=site,
|
||||
)
|
||||
|
||||
|
||||
class HumanInputFormSubmitResponse(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
class HumanInputFormSubmitResponse(ResponseModel):
|
||||
pass
|
||||
|
||||
|
||||
register_schema_models(web_ns, HumanInputFormSubmitPayload)
|
||||
@ -86,40 +107,26 @@ def _create_upload_service() -> HumanInputFileUploadService:
|
||||
)
|
||||
|
||||
|
||||
class FormDefinitionPayload(TypedDict):
|
||||
form_content: Any
|
||||
inputs: Any
|
||||
resolved_default_values: dict[str, str]
|
||||
user_actions: Any
|
||||
expiration_time: int
|
||||
site: NotRequired[dict]
|
||||
|
||||
|
||||
def _jsonify_form_definition(
|
||||
form: Form,
|
||||
*,
|
||||
inputs: Sequence[FormInputConfig] = (),
|
||||
site_payload: dict | None = None,
|
||||
) -> Response:
|
||||
"""Return the form payload (optionally with site) as a JSON response."""
|
||||
definition_payload = form.get_definition().model_dump(mode="json")
|
||||
payload: FormDefinitionPayload = {
|
||||
"form_content": definition_payload["rendered_content"],
|
||||
"inputs": [i.model_dump(mode="json") for i in inputs],
|
||||
"resolved_default_values": stringify_form_default_values(definition_payload["default_values"]),
|
||||
"user_actions": definition_payload["user_actions"],
|
||||
"expiration_time": to_timestamp(form.expiration_time),
|
||||
}
|
||||
if site_payload is not None:
|
||||
payload["site"] = site_payload
|
||||
return Response(json.dumps(payload, ensure_ascii=False), mimetype="application/json")
|
||||
|
||||
|
||||
@web_ns.route("/form/human_input/<string:form_token>/upload-token")
|
||||
class HumanInputFormUploadTokenApi(Resource):
|
||||
"""API for issuing HITL upload tokens for active human input forms."""
|
||||
|
||||
@web_ns.response(200, "Success", web_ns.models[HumanInputUploadTokenResponse.__name__])
|
||||
@web_ns.doc("create_human_input_form_upload_token")
|
||||
@web_ns.doc(description="Issue an upload token for an active human input form")
|
||||
@web_ns.doc(params={"form_token": "Human input form token"})
|
||||
@web_ns.doc(
|
||||
responses={
|
||||
200: "Upload token issued successfully",
|
||||
404: "Form not found",
|
||||
412: "Form already submitted or expired",
|
||||
429: "Too many requests",
|
||||
}
|
||||
)
|
||||
@web_ns.response(
|
||||
200,
|
||||
"Upload token issued successfully",
|
||||
web_ns.models[HumanInputUploadTokenResponse.__name__],
|
||||
)
|
||||
def post(self, form_token: str):
|
||||
"""
|
||||
Issue an upload token for a human input form.
|
||||
@ -136,11 +143,9 @@ class HumanInputFormUploadTokenApi(Resource):
|
||||
except FormNotFoundError:
|
||||
raise NotFoundError("Form not found")
|
||||
|
||||
response = HumanInputUploadTokenResponse(
|
||||
upload_token=token.upload_token,
|
||||
expires_at=to_timestamp(token.expires_at),
|
||||
)
|
||||
return response.model_dump(mode="json"), 200
|
||||
return HumanInputUploadTokenResponse(
|
||||
upload_token=token.upload_token, expires_at=to_timestamp(token.expires_at)
|
||||
).model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@web_ns.route("/form/human_input/<string:form_token>")
|
||||
@ -150,7 +155,23 @@ class HumanInputFormApi(Resource):
|
||||
# NOTE(QuantumGhost): this endpoint is unauthenticated on purpose for now.
|
||||
|
||||
# def get(self, _app_model: App, _end_user: EndUser, form_token: str):
|
||||
@web_ns.response(200, "Success", web_ns.models[HumanInputFormDefinitionResponse.__name__])
|
||||
@web_ns.doc("get_human_input_form")
|
||||
@web_ns.doc(description="Get a human input form definition by token")
|
||||
@web_ns.doc(params={"form_token": "Human input form token"})
|
||||
@web_ns.doc(
|
||||
responses={
|
||||
200: "Form retrieved successfully",
|
||||
403: "Forbidden",
|
||||
404: "Form not found",
|
||||
412: "Form already submitted or expired",
|
||||
429: "Too many requests",
|
||||
}
|
||||
)
|
||||
@web_ns.response(
|
||||
200,
|
||||
"Form retrieved successfully",
|
||||
web_ns.models[HumanInputFormDefinitionResponse.__name__],
|
||||
)
|
||||
def get(self, form_token: str):
|
||||
"""
|
||||
Get human input form definition by token.
|
||||
@ -172,17 +193,47 @@ class HumanInputFormApi(Resource):
|
||||
|
||||
service.ensure_form_active(form)
|
||||
app_model, site = _get_app_site_from_form(form)
|
||||
tenant = app_model.tenant
|
||||
if tenant is None:
|
||||
raise Forbidden()
|
||||
inputs = service.resolve_form_inputs(form)
|
||||
|
||||
return _jsonify_form_definition(
|
||||
form,
|
||||
inputs=inputs,
|
||||
site_payload=serialize_app_site_payload(app_model, site, None),
|
||||
return dump_response(
|
||||
HumanInputFormDefinitionResponse,
|
||||
HumanInputFormDefinitionResponse.from_form(
|
||||
form,
|
||||
inputs=inputs,
|
||||
site=WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
site=site,
|
||||
end_user_id=None,
|
||||
can_replace_logo=FeatureService.get_features(
|
||||
app_model.tenant_id, exclude_vector_space=True
|
||||
).can_replace_logo,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
# def post(self, _app_model: App, _end_user: EndUser, form_token: str):
|
||||
@web_ns.response(200, "Success", web_ns.models[HumanInputFormSubmitResponse.__name__])
|
||||
@web_ns.expect(web_ns.models[HumanInputFormSubmitPayload.__name__])
|
||||
@web_ns.doc("submit_human_input_form")
|
||||
@web_ns.doc(description="Submit a human input form by token")
|
||||
@web_ns.doc(params={"form_token": "Human input form token"})
|
||||
@web_ns.doc(
|
||||
responses={
|
||||
200: "Form submitted successfully",
|
||||
400: "Bad request - invalid submission data",
|
||||
404: "Form not found",
|
||||
412: "Form already submitted or expired",
|
||||
429: "Too many requests",
|
||||
}
|
||||
)
|
||||
@web_ns.response(
|
||||
200,
|
||||
"Form submitted successfully",
|
||||
web_ns.models[HumanInputFormSubmitResponse.__name__],
|
||||
)
|
||||
def post(self, form_token: str):
|
||||
"""
|
||||
Submit human input form by token.
|
||||
@ -225,7 +276,7 @@ class HumanInputFormApi(Resource):
|
||||
except FormNotFoundError:
|
||||
raise NotFoundError("Form not found")
|
||||
|
||||
return {}, 200
|
||||
return HumanInputFormSubmitResponse().model_dump(mode="json"), 200
|
||||
|
||||
|
||||
def _get_app_site_from_form(form: Form) -> tuple[App, Site]:
|
||||
@ -238,7 +289,7 @@ def _get_app_site_from_form(form: Form) -> tuple[App, Site]:
|
||||
if site is None:
|
||||
raise Forbidden()
|
||||
|
||||
if app_model.tenant and app_model.tenant.status == TenantStatus.ARCHIVE:
|
||||
if app_model.tenant is None or app_model.tenant.status == TenantStatus.ARCHIVE:
|
||||
raise Forbidden()
|
||||
|
||||
return app_model, site
|
||||
|
||||
@ -7,7 +7,6 @@ from pydantic import BaseModel, Field, TypeAdapter
|
||||
from werkzeug.exceptions import InternalServerError, NotFound
|
||||
|
||||
from controllers.common.controller_schemas import MessageFeedbackPayload, MessageListQuery
|
||||
from controllers.common.fields import GeneratedAppResponse
|
||||
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
||||
from controllers.web import web_ns
|
||||
from controllers.web.error import (
|
||||
@ -51,7 +50,6 @@ class MessageMoreLikeThisQuery(BaseModel):
|
||||
register_schema_models(web_ns, MessageListQuery, MessageFeedbackPayload, MessageMoreLikeThisQuery)
|
||||
register_response_schema_models(
|
||||
web_ns,
|
||||
GeneratedAppResponse,
|
||||
ResultResponse,
|
||||
SuggestedQuestionsResponse,
|
||||
WebMessageInfiniteScrollPagination,
|
||||
@ -161,7 +159,7 @@ class MessageMoreLikeThisApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[GeneratedAppResponse.__name__])
|
||||
@web_ns.response(200, "Success")
|
||||
def get(self, app_model: App, end_user: EndUser, message_id: UUID):
|
||||
if app_model.mode != "completion":
|
||||
raise NotCompletionAppError()
|
||||
@ -182,6 +180,7 @@ class MessageMoreLikeThisApi(WebApiResource):
|
||||
streaming=streaming,
|
||||
)
|
||||
|
||||
# response-contract:ignore compact_generate_response
|
||||
return helper.compact_generate_response(response)
|
||||
except MessageNotExistsError:
|
||||
raise NotFound("Message Not Exists.")
|
||||
|
||||
@ -65,11 +65,10 @@ class RemoteFileInfoApi(WebApiResource):
|
||||
# failed back to get method
|
||||
resp = remote_fetcher.make_request("GET", decoded_url, timeout=3)
|
||||
resp.raise_for_status()
|
||||
info = RemoteFileInfo(
|
||||
return RemoteFileInfo(
|
||||
file_type=resp.headers.get("Content-Type", "application/octet-stream"),
|
||||
file_length=int(resp.headers.get("Content-Length", -1)),
|
||||
)
|
||||
return info.model_dump(mode="json")
|
||||
).model_dump(mode="json")
|
||||
|
||||
|
||||
@web_ns.route("/remote-files/upload")
|
||||
@ -141,7 +140,7 @@ class RemoteFileUploadApi(WebApiResource):
|
||||
except services.errors.file.UnsupportedFileTypeError:
|
||||
raise UnsupportedFileTypeError
|
||||
|
||||
payload1 = FileWithSignedUrl(
|
||||
return FileWithSignedUrl(
|
||||
id=upload_file.id,
|
||||
name=upload_file.name,
|
||||
size=upload_file.size,
|
||||
@ -150,5 +149,4 @@ class RemoteFileUploadApi(WebApiResource):
|
||||
mime_type=upload_file.mime_type,
|
||||
created_by=upload_file.created_by,
|
||||
created_at=int(upload_file.created_at.timestamp()),
|
||||
)
|
||||
return payload1.model_dump(mode="json"), 201
|
||||
).model_dump(mode="json"), 201
|
||||
|
||||
@ -49,9 +49,7 @@ class SavedMessageListApi(WebApiResource):
|
||||
adapter = TypeAdapter(SavedMessageItem)
|
||||
items = [adapter.validate_python(message, from_attributes=True) for message in pagination.data]
|
||||
return SavedMessageInfiniteScrollPagination(
|
||||
limit=pagination.limit,
|
||||
has_more=pagination.has_more,
|
||||
data=items,
|
||||
limit=pagination.limit, has_more=pagination.has_more, data=items
|
||||
).model_dump(mode="json")
|
||||
|
||||
@web_ns.doc("Save Message")
|
||||
@ -102,6 +100,7 @@ class SavedMessageApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(204, "Message removed successfully")
|
||||
def delete(self, app_model: App, end_user: EndUser, message_id: UUID):
|
||||
message_id_str = str(message_id)
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
from typing import Any, cast
|
||||
from typing import Any, Self
|
||||
|
||||
from flask_restx import fields, marshal, marshal_with
|
||||
from pydantic import Field
|
||||
from pydantic import AliasChoices, Field, computed_field
|
||||
from sqlalchemy import select
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
@ -11,30 +10,19 @@ from controllers.web import web_ns
|
||||
from controllers.web.wraps import WebApiResource
|
||||
from extensions.ext_database import db
|
||||
from fields.base import ResponseModel
|
||||
from libs.helper import AppIconUrlField
|
||||
from models.account import TenantStatus
|
||||
from libs.helper import build_icon_url
|
||||
from models.account import Tenant, TenantStatus
|
||||
from models.model import App, EndUser, Site
|
||||
from services.feature_service import FeatureService
|
||||
|
||||
|
||||
class AppSiteModelConfigResponse(ResponseModel):
|
||||
opening_statement: str | None = None
|
||||
suggested_questions: Any
|
||||
suggested_questions_after_answer: Any
|
||||
more_like_this: Any
|
||||
model: Any
|
||||
user_input_form: Any
|
||||
pre_prompt: str | None = None
|
||||
|
||||
|
||||
class AppSiteResponse(ResponseModel):
|
||||
title: str | None = None
|
||||
class WebSiteResponse(ResponseModel):
|
||||
title: str
|
||||
chat_color_theme: str | None = None
|
||||
chat_color_theme_inverted: bool | None = None
|
||||
chat_color_theme_inverted: bool
|
||||
icon_type: str | None = None
|
||||
icon: str | None = None
|
||||
icon_background: str | None = None
|
||||
icon_url: str | None = None
|
||||
description: str | None = None
|
||||
copyright: str | None = None
|
||||
privacy_policy: str | None = None
|
||||
@ -44,64 +32,92 @@ class AppSiteResponse(ResponseModel):
|
||||
show_workflow_steps: bool | None = None
|
||||
use_icon_as_answer_icon: bool | None = None
|
||||
|
||||
@computed_field(return_type=str | None) # type: ignore[prop-decorator]
|
||||
@property
|
||||
def icon_url(self) -> str | None:
|
||||
return build_icon_url(self.icon_type, self.icon)
|
||||
|
||||
class AppSiteInfoResponse(ResponseModel):
|
||||
|
||||
class WebModelConfigResponse(ResponseModel):
|
||||
opening_statement: str | None = None
|
||||
suggested_questions: Any = Field(
|
||||
default=None,
|
||||
validation_alias=AliasChoices("suggested_questions_list", "suggested_questions"),
|
||||
)
|
||||
suggested_questions_after_answer: Any = Field(
|
||||
default=None,
|
||||
validation_alias=AliasChoices("suggested_questions_after_answer_dict", "suggested_questions_after_answer"),
|
||||
)
|
||||
more_like_this: Any = Field(
|
||||
default=None,
|
||||
validation_alias=AliasChoices("more_like_this_dict", "more_like_this"),
|
||||
)
|
||||
model: Any = Field(default=None, validation_alias=AliasChoices("model_dict", "model"))
|
||||
user_input_form: Any = Field(
|
||||
default=None,
|
||||
validation_alias=AliasChoices("user_input_form_list", "user_input_form"),
|
||||
)
|
||||
pre_prompt: str | None = None
|
||||
|
||||
|
||||
class WebAppCustomConfigResponse(ResponseModel):
|
||||
remove_webapp_brand: bool
|
||||
replace_webapp_logo: str | None = None
|
||||
|
||||
|
||||
class WebAppSiteResponse(ResponseModel):
|
||||
app_id: str
|
||||
end_user_id: str | None = None
|
||||
enable_site: bool
|
||||
site: AppSiteResponse
|
||||
model_config_: AppSiteModelConfigResponse | None = Field(default=None, alias="model_config")
|
||||
plan: str | None = None
|
||||
site: WebSiteResponse
|
||||
model_config_: WebModelConfigResponse | None = Field(
|
||||
default=None, validation_alias="model_config", serialization_alias="model_config"
|
||||
)
|
||||
plan: str
|
||||
can_replace_logo: bool
|
||||
custom_config: dict[str, Any] | None = Field(default=None)
|
||||
custom_config: WebAppCustomConfigResponse | None = None
|
||||
|
||||
@classmethod
|
||||
def from_app_site(
|
||||
cls,
|
||||
*,
|
||||
tenant: Tenant,
|
||||
app_model: App,
|
||||
site: Site,
|
||||
end_user_id: str | None,
|
||||
can_replace_logo: bool,
|
||||
) -> Self:
|
||||
custom_config = None
|
||||
if can_replace_logo:
|
||||
replace_webapp_logo = (
|
||||
f"{dify_config.FILES_URL}/files/workspaces/{tenant.id}/webapp-logo"
|
||||
if tenant.custom_config_dict.get("replace_webapp_logo")
|
||||
else None
|
||||
)
|
||||
custom_config = WebAppCustomConfigResponse(
|
||||
remove_webapp_brand=tenant.custom_config_dict.get("remove_webapp_brand", False),
|
||||
replace_webapp_logo=replace_webapp_logo,
|
||||
)
|
||||
|
||||
return cls(
|
||||
app_id=app_model.id,
|
||||
end_user_id=end_user_id,
|
||||
enable_site=app_model.enable_site,
|
||||
site=WebSiteResponse.model_validate(site, from_attributes=True),
|
||||
model_config_=None,
|
||||
plan=tenant.plan,
|
||||
can_replace_logo=can_replace_logo,
|
||||
custom_config=custom_config,
|
||||
)
|
||||
|
||||
|
||||
register_response_schema_models(web_ns, AppSiteInfoResponse)
|
||||
register_response_schema_models(
|
||||
web_ns, WebSiteResponse, WebModelConfigResponse, WebAppCustomConfigResponse, WebAppSiteResponse
|
||||
)
|
||||
|
||||
|
||||
@web_ns.route("/site")
|
||||
class AppSiteApi(WebApiResource):
|
||||
"""Resource for app sites."""
|
||||
|
||||
model_config_fields = {
|
||||
"opening_statement": fields.String,
|
||||
"suggested_questions": fields.Raw(attribute="suggested_questions_list"),
|
||||
"suggested_questions_after_answer": fields.Raw(attribute="suggested_questions_after_answer_dict"),
|
||||
"more_like_this": fields.Raw(attribute="more_like_this_dict"),
|
||||
"model": fields.Raw(attribute="model_dict"),
|
||||
"user_input_form": fields.Raw(attribute="user_input_form_list"),
|
||||
"pre_prompt": fields.String,
|
||||
}
|
||||
|
||||
site_fields = {
|
||||
"title": fields.String,
|
||||
"chat_color_theme": fields.String,
|
||||
"chat_color_theme_inverted": fields.Boolean,
|
||||
"icon_type": fields.String,
|
||||
"icon": fields.String,
|
||||
"icon_background": fields.String,
|
||||
"icon_url": AppIconUrlField,
|
||||
"description": fields.String,
|
||||
"copyright": fields.String,
|
||||
"privacy_policy": fields.String,
|
||||
"custom_disclaimer": fields.String,
|
||||
"default_language": fields.String,
|
||||
"prompt_public": fields.Boolean,
|
||||
"show_workflow_steps": fields.Boolean,
|
||||
"use_icon_as_answer_icon": fields.Boolean,
|
||||
}
|
||||
|
||||
app_fields = {
|
||||
"app_id": fields.String,
|
||||
"end_user_id": fields.String,
|
||||
"enable_site": fields.Boolean,
|
||||
"site": fields.Nested(site_fields),
|
||||
"model_config": fields.Nested(model_config_fields, allow_null=True),
|
||||
"plan": fields.String,
|
||||
"can_replace_logo": fields.Boolean,
|
||||
"custom_config": fields.Raw(attribute="custom_config"),
|
||||
}
|
||||
|
||||
@web_ns.doc("Get App Site Info")
|
||||
@web_ns.doc(description="Retrieve app site information and configuration.")
|
||||
@web_ns.doc(
|
||||
@ -114,57 +130,25 @@ class AppSiteApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[AppSiteInfoResponse.__name__])
|
||||
@marshal_with(app_fields)
|
||||
@web_ns.response(200, "Success", web_ns.models[WebAppSiteResponse.__name__])
|
||||
def get(self, app_model: App, end_user: EndUser):
|
||||
"""Retrieve app site info."""
|
||||
# get site
|
||||
site = db.session.scalar(select(Site).where(Site.app_id == app_model.id).limit(1))
|
||||
|
||||
if not site:
|
||||
if site is None:
|
||||
raise Forbidden()
|
||||
|
||||
if app_model.tenant and app_model.tenant.status == TenantStatus.ARCHIVE:
|
||||
tenant = app_model.tenant
|
||||
if tenant is None or tenant.status == TenantStatus.ARCHIVE:
|
||||
raise Forbidden()
|
||||
|
||||
can_replace_logo = FeatureService.get_features(app_model.tenant_id, exclude_vector_space=True).can_replace_logo
|
||||
|
||||
return AppSiteInfo(app_model.tenant, app_model, site, end_user.id, can_replace_logo)
|
||||
|
||||
|
||||
class AppSiteInfo:
|
||||
"""Class to store site information."""
|
||||
|
||||
def __init__(self, tenant, app, site, end_user, can_replace_logo):
|
||||
"""Initialize AppSiteInfo instance."""
|
||||
self.app_id = app.id
|
||||
self.end_user_id = end_user
|
||||
self.enable_site = app.enable_site
|
||||
self.site = site
|
||||
self.model_config = None
|
||||
self.plan = tenant.plan
|
||||
self.can_replace_logo = can_replace_logo
|
||||
|
||||
if can_replace_logo:
|
||||
base_url = dify_config.FILES_URL
|
||||
remove_webapp_brand = tenant.custom_config_dict.get("remove_webapp_brand", False)
|
||||
replace_webapp_logo = (
|
||||
f"{base_url}/files/workspaces/{tenant.id}/webapp-logo"
|
||||
if tenant.custom_config_dict.get("replace_webapp_logo")
|
||||
else None
|
||||
)
|
||||
self.custom_config = {
|
||||
"remove_webapp_brand": remove_webapp_brand,
|
||||
"replace_webapp_logo": replace_webapp_logo,
|
||||
}
|
||||
|
||||
|
||||
def serialize_site(site: Site) -> dict[str, Any]:
|
||||
"""Serialize Site model using the same schema as AppSiteApi."""
|
||||
return cast(dict[str, Any], marshal(site, AppSiteApi.site_fields))
|
||||
|
||||
|
||||
def serialize_app_site_payload(app_model: App, site: Site, end_user_id: str | None) -> dict[str, Any]:
|
||||
can_replace_logo = FeatureService.get_features(app_model.tenant_id, exclude_vector_space=True).can_replace_logo
|
||||
app_site_info = AppSiteInfo(app_model.tenant, app_model, site, end_user_id, can_replace_logo)
|
||||
return cast(dict[str, Any], marshal(app_site_info, AppSiteApi.app_fields))
|
||||
return WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
site=site,
|
||||
end_user_id=end_user.id,
|
||||
can_replace_logo=can_replace_logo,
|
||||
).model_dump(mode="json")
|
||||
|
||||
@ -3,7 +3,7 @@ import logging
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
from controllers.common.controller_schemas import WorkflowRunPayload
|
||||
from controllers.common.fields import GeneratedAppResponse, SimpleResultResponse
|
||||
from controllers.common.fields import SimpleResultResponse
|
||||
from controllers.common.schema import register_response_schema_models, register_schema_models
|
||||
from controllers.web import web_ns
|
||||
from controllers.web.error import (
|
||||
@ -33,7 +33,7 @@ from services.errors.llm import InvokeRateLimitError
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
register_schema_models(web_ns, WorkflowRunPayload)
|
||||
register_response_schema_models(web_ns, GeneratedAppResponse, SimpleResultResponse)
|
||||
register_response_schema_models(web_ns, SimpleResultResponse)
|
||||
|
||||
|
||||
@web_ns.route("/workflows/run")
|
||||
@ -51,7 +51,7 @@ class WorkflowRunApi(WebApiResource):
|
||||
500: "Internal Server Error",
|
||||
}
|
||||
)
|
||||
@web_ns.response(200, "Success", web_ns.models[GeneratedAppResponse.__name__])
|
||||
@web_ns.response(200, "Workflow run stream started")
|
||||
def post(self, app_model: App, end_user: EndUser):
|
||||
"""
|
||||
Run workflow
|
||||
@ -68,6 +68,7 @@ class WorkflowRunApi(WebApiResource):
|
||||
app_model=app_model, user=end_user, args=args, invoke_from=InvokeFrom.WEB_APP, streaming=True
|
||||
)
|
||||
|
||||
# response-contract:ignore compact_generate_response
|
||||
return helper.compact_generate_response(response)
|
||||
except ProviderTokenNotInitError as ex:
|
||||
raise ProviderNotInitializeError(ex.description)
|
||||
@ -121,4 +122,4 @@ class WorkflowTaskStopApi(WebApiResource):
|
||||
# New graph engine command channel mechanism
|
||||
GraphEngineManager(redis_client).send_stop_command(task_id)
|
||||
|
||||
return {"result": "success"}
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import re
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
from typing import Literal, overload
|
||||
from urllib.parse import unquote
|
||||
|
||||
from configs import dify_config
|
||||
@ -40,10 +40,22 @@ USER_AGENT = (
|
||||
|
||||
|
||||
class ExtractProcessor:
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: Literal[True], is_automatic: bool = False
|
||||
) -> str: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: Literal[False] = False, is_automatic: bool = False
|
||||
) -> list[Document]: ...
|
||||
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: bool = False, is_automatic: bool = False
|
||||
) -> Union[list[Document], str]:
|
||||
) -> list[Document] | str:
|
||||
extract_setting = ExtractSetting(
|
||||
datasource_type=DatasourceType.FILE, upload_file=upload_file, document_model="text_model"
|
||||
)
|
||||
@ -53,8 +65,16 @@ class ExtractProcessor:
|
||||
else:
|
||||
return cls.extract(extract_setting, is_automatic)
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: bool = False) -> Union[list[Document], str]:
|
||||
def load_from_url(cls, url: str, return_text: Literal[True]) -> str: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: Literal[False] = False) -> list[Document]: ...
|
||||
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: bool = False) -> list[Document] | str:
|
||||
response = remote_fetcher.make_request("GET", url, headers={"User-Agent": USER_AGENT})
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
|
||||
@ -11,14 +11,14 @@ from libs.helper import to_timestamp
|
||||
class UploadConfig(ResponseModel):
|
||||
file_size_limit: int
|
||||
batch_count_limit: int
|
||||
file_upload_limit: int | None = None
|
||||
file_upload_limit: int
|
||||
image_file_size_limit: int
|
||||
video_file_size_limit: int
|
||||
audio_file_size_limit: int
|
||||
workflow_file_upload_limit: int
|
||||
image_file_batch_limit: int
|
||||
single_chunk_attachment_limit: int
|
||||
attachment_image_file_size_limit: int | None = None
|
||||
attachment_image_file_size_limit: int
|
||||
|
||||
|
||||
class FileResponse(ResponseModel):
|
||||
|
||||
@ -13429,7 +13429,6 @@ Soft lifecycle state for Agent records.
|
||||
| created_at | integer | | No |
|
||||
| files | [ string ] | | Yes |
|
||||
| id | string | | Yes |
|
||||
| message_chain_id | string | | No |
|
||||
| message_id | string | | Yes |
|
||||
| observation | string | | No |
|
||||
| position | integer | | Yes |
|
||||
@ -14540,8 +14539,8 @@ Enum class for configurate method of provider model.
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| annotation_create_account | [SimpleAccount](#simpleaccount) | | No |
|
||||
| annotation_id | string | | Yes |
|
||||
| created_at | integer | | No |
|
||||
| id | string | | Yes |
|
||||
|
||||
#### ConversationDetail
|
||||
|
||||
@ -17079,6 +17078,7 @@ Enum class for large language model mode.
|
||||
| agent_thoughts | [ [AgentThought](#agentthought) ] | | No |
|
||||
| annotation | [ConversationAnnotation](#conversationannotation) | | No |
|
||||
| annotation_hit_history | [ConversationAnnotationHitHistory](#conversationannotationhithistory) | | No |
|
||||
| answer | string | | Yes |
|
||||
| answer_tokens | integer | | No |
|
||||
| conversation_id | string | | Yes |
|
||||
| created_at | integer | | No |
|
||||
@ -17092,12 +17092,11 @@ Enum class for large language model mode.
|
||||
| inputs | object | | Yes |
|
||||
| message | [JSONValue](#jsonvalue) | | No |
|
||||
| message_files | [ [MessageFile](#messagefile) ] | | No |
|
||||
| message_metadata_dict | [JSONValue](#jsonvalue) | | No |
|
||||
| message_tokens | integer | | No |
|
||||
| metadata | [JSONValue](#jsonvalue) | | No |
|
||||
| parent_message_id | string | | No |
|
||||
| provider_response_latency | number | | No |
|
||||
| query | string | | Yes |
|
||||
| re_sign_file_url_answer | string | | Yes |
|
||||
| status | string | | Yes |
|
||||
| workflow_run_id | string | | No |
|
||||
|
||||
@ -20187,11 +20186,11 @@ Payload for updating a snippet.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| attachment_image_file_size_limit | integer | | No |
|
||||
| attachment_image_file_size_limit | integer | | Yes |
|
||||
| audio_file_size_limit | integer | | Yes |
|
||||
| batch_count_limit | integer | | Yes |
|
||||
| file_size_limit | integer | | Yes |
|
||||
| file_upload_limit | integer | | No |
|
||||
| file_upload_limit | integer | | Yes |
|
||||
| image_file_batch_limit | integer | | Yes |
|
||||
| image_file_size_limit | integer | | Yes |
|
||||
| single_chunk_attachment_limit | integer | | Yes |
|
||||
|
||||
@ -21,7 +21,7 @@ Convert audio file to text using speech-to-text service.
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [AudioTranscriptResponse](#audiotranscriptresponse)<br> |
|
||||
| 200 | Success | **application/json**: [AudioToTextResponse](#audiototextresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
@ -40,14 +40,14 @@ Create a chat message for conversational applications.
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
| 404 | App Not Found | |
|
||||
| 500 | Internal Server Error | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request |
|
||||
| 401 | Unauthorized |
|
||||
| 403 | Forbidden |
|
||||
| 404 | App Not Found |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
### [POST] /chat-messages/{task_id}/stop
|
||||
Stop a running chat message task.
|
||||
@ -80,14 +80,14 @@ Create a completion message for text generation applications.
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
| 404 | App Not Found | |
|
||||
| 500 | Internal Server Error | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request |
|
||||
| 401 | Unauthorized |
|
||||
| 403 | Forbidden |
|
||||
| 404 | App Not Found |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
### [POST] /completion-messages/{task_id}/stop
|
||||
Stop a running completion message task.
|
||||
@ -346,23 +346,29 @@ Verify password reset token validity
|
||||
### [GET] /form/human_input/{form_token}
|
||||
**Get human input form definition by token**
|
||||
|
||||
Get a human input form definition by token
|
||||
GET /api/form/human_input/<form_token>
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Located in | Description | Required | Schema |
|
||||
| ---- | ---------- | ----------- | -------- | ------ |
|
||||
| form_token | path | | Yes | string |
|
||||
| form_token | path | Human input form token | Yes | string |
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [HumanInputFormDefinitionResponse](#humaninputformdefinitionresponse)<br> |
|
||||
| 200 | Form retrieved successfully | **application/json**: [HumanInputFormDefinitionResponse](#humaninputformdefinitionresponse)<br> |
|
||||
| 403 | Forbidden | |
|
||||
| 404 | Form not found | |
|
||||
| 412 | Form already submitted or expired | |
|
||||
| 429 | Too many requests | |
|
||||
|
||||
### [POST] /form/human_input/{form_token}
|
||||
**Submit human input form by token**
|
||||
|
||||
Submit a human input form by token
|
||||
POST /api/form/human_input/<form_token>
|
||||
|
||||
Request body:
|
||||
@ -377,7 +383,7 @@ Request body:
|
||||
|
||||
| Name | Located in | Description | Required | Schema |
|
||||
| ---- | ---------- | ----------- | -------- | ------ |
|
||||
| form_token | path | | Yes | string |
|
||||
| form_token | path | Human input form token | Yes | string |
|
||||
|
||||
#### Request Body
|
||||
|
||||
@ -389,24 +395,32 @@ Request body:
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [HumanInputFormSubmitResponse](#humaninputformsubmitresponse)<br> |
|
||||
| 200 | Form submitted successfully | **application/json**: [HumanInputFormSubmitResponse](#humaninputformsubmitresponse)<br> |
|
||||
| 400 | Bad request - invalid submission data | |
|
||||
| 404 | Form not found | |
|
||||
| 412 | Form already submitted or expired | |
|
||||
| 429 | Too many requests | |
|
||||
|
||||
### [POST] /form/human_input/{form_token}/upload-token
|
||||
**Issue an upload token for a human input form**
|
||||
|
||||
Issue an upload token for an active human input form
|
||||
POST /api/form/human_input/<form_token>/upload-token
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Located in | Description | Required | Schema |
|
||||
| ---- | ---------- | ----------- | -------- | ------ |
|
||||
| form_token | path | | Yes | string |
|
||||
| form_token | path | Human input form token | Yes | string |
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [HumanInputUploadTokenResponse](#humaninputuploadtokenresponse)<br> |
|
||||
| 200 | Upload token issued successfully | **application/json**: [HumanInputUploadTokenResponse](#humaninputuploadtokenresponse)<br> |
|
||||
| 404 | Form not found | |
|
||||
| 412 | Form already submitted or expired | |
|
||||
| 429 | Too many requests | |
|
||||
|
||||
### [POST] /human-input-forms/files
|
||||
**Upload one local file or remote URL file for a HITL human input form**
|
||||
@ -526,14 +540,14 @@ Generate a new completion similar to an existing message (completion apps only).
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
||||
| 400 | Bad Request - Not a completion app or feature disabled | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
| 404 | Message Not Found | |
|
||||
| 500 | Internal Server Error | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request - Not a completion app or feature disabled |
|
||||
| 401 | Unauthorized |
|
||||
| 403 | Forbidden |
|
||||
| 404 | Message Not Found |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
### [GET] /messages/{message_id}/suggested-questions
|
||||
Get suggested follow-up questions after a message (chat apps only).
|
||||
@ -752,7 +766,7 @@ Retrieve app site information and configuration.
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [AppSiteInfoResponse](#appsiteinforesponse)<br> |
|
||||
| 200 | Success | **application/json**: [WebAppSiteResponse](#webappsiteresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
@ -799,13 +813,13 @@ Convert text to audio using text-to-speech service.
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [AudioBinaryResponse](#audiobinaryresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
| 500 | Internal Server Error | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request |
|
||||
| 401 | Unauthorized |
|
||||
| 403 | Forbidden |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
### [GET] /webapp/access-mode
|
||||
Retrieve the access mode for a web application (public or restricted).
|
||||
@ -856,14 +870,14 @@ Execute a workflow with provided inputs and files.
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Success | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
||||
| 400 | Bad Request | |
|
||||
| 401 | Unauthorized | |
|
||||
| 403 | Forbidden | |
|
||||
| 404 | App Not Found | |
|
||||
| 500 | Internal Server Error | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request |
|
||||
| 401 | Unauthorized |
|
||||
| 403 | Forbidden |
|
||||
| 404 | App Not Found |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
### [POST] /workflows/tasks/{task_id}/stop
|
||||
**Stop workflow task**
|
||||
@ -967,58 +981,7 @@ Returns Server-Sent Events stream.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| appId | string | Application ID | Yes |
|
||||
|
||||
#### AppSiteInfoResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| app_id | string | | Yes |
|
||||
| can_replace_logo | boolean | | Yes |
|
||||
| custom_config | object | | No |
|
||||
| enable_site | boolean | | Yes |
|
||||
| end_user_id | string | | No |
|
||||
| model_config | [AppSiteModelConfigResponse](#appsitemodelconfigresponse) | | No |
|
||||
| plan | string | | No |
|
||||
| site | [AppSiteResponse](#appsiteresponse) | | Yes |
|
||||
|
||||
#### AppSiteModelConfigResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| model | | | Yes |
|
||||
| more_like_this | | | Yes |
|
||||
| opening_statement | string | | No |
|
||||
| pre_prompt | string | | No |
|
||||
| suggested_questions | | | Yes |
|
||||
| suggested_questions_after_answer | | | Yes |
|
||||
| user_input_form | | | Yes |
|
||||
|
||||
#### AppSiteResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| chat_color_theme | string | | No |
|
||||
| chat_color_theme_inverted | boolean | | No |
|
||||
| copyright | string | | No |
|
||||
| custom_disclaimer | string | | No |
|
||||
| default_language | string | | No |
|
||||
| description | string | | No |
|
||||
| icon | string | | No |
|
||||
| icon_background | string | | No |
|
||||
| icon_type | string | | No |
|
||||
| icon_url | string | | No |
|
||||
| privacy_policy | string | | No |
|
||||
| prompt_public | boolean | | No |
|
||||
| show_workflow_steps | boolean | | No |
|
||||
| title | string | | No |
|
||||
| use_icon_as_answer_icon | boolean | | No |
|
||||
|
||||
#### AudioBinaryResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| AudioBinaryResponse | string | | |
|
||||
|
||||
#### AudioTranscriptResponse
|
||||
#### AudioToTextResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
@ -1216,12 +1179,6 @@ Button styles for user actions.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| FormInputConfig | [ParagraphInputConfig](#paragraphinputconfig)<br>[SelectInputConfig](#selectinputconfig)<br>[FileInputConfig](#fileinputconfig)<br>[FileListInputConfig](#filelistinputconfig) | | |
|
||||
|
||||
#### GeneratedAppResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| GeneratedAppResponse | | | |
|
||||
|
||||
#### HumanInputContent
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -1260,11 +1217,11 @@ Parsed multipart form fields for HITL uploads.
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| expiration_time | integer | | Yes |
|
||||
| form_content | | | Yes |
|
||||
| inputs | | | Yes |
|
||||
| form_content | string | | Yes |
|
||||
| inputs | [ [FormInputConfig](#forminputconfig) ] | | Yes |
|
||||
| resolved_default_values | object | | Yes |
|
||||
| site | object | | No |
|
||||
| user_actions | | | Yes |
|
||||
| site | [WebAppSiteResponse](#webappsiteresponse) | | No |
|
||||
| user_actions | [ [UserActionConfig](#useractionconfig) ] | | Yes |
|
||||
|
||||
#### HumanInputFormSubmissionData
|
||||
|
||||
@ -1306,7 +1263,7 @@ Parsed multipart form fields for HITL uploads.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| JSONValue | string<br>integer<br>number<br>boolean<br>object<br>[ object ] | | |
|
||||
| JSONValue | | | |
|
||||
|
||||
#### JSONValueType
|
||||
|
||||
@ -1681,6 +1638,26 @@ in form definiton, or a variable while the workflow is running.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| protocol | string | | Yes |
|
||||
|
||||
#### WebAppCustomConfigResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| remove_webapp_brand | boolean | | Yes |
|
||||
| replace_webapp_logo | string | | No |
|
||||
|
||||
#### WebAppSiteResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| app_id | string | | Yes |
|
||||
| can_replace_logo | boolean | | Yes |
|
||||
| custom_config | [WebAppCustomConfigResponse](#webappcustomconfigresponse) | | No |
|
||||
| enable_site | boolean | | Yes |
|
||||
| end_user_id | string | | No |
|
||||
| model_config | [WebModelConfigResponse](#webmodelconfigresponse) | | No |
|
||||
| plan | string | | Yes |
|
||||
| site | [WebSiteResponse](#websiteresponse) | | Yes |
|
||||
|
||||
#### WebMessageInfiniteScrollPagination
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -1709,6 +1686,38 @@ in form definiton, or a variable while the workflow is running.
|
||||
| retriever_resources | [ [RetrieverResource](#retrieverresource) ] | | Yes |
|
||||
| status | string | | Yes |
|
||||
|
||||
#### WebModelConfigResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| model | | | No |
|
||||
| more_like_this | | | No |
|
||||
| opening_statement | string | | No |
|
||||
| pre_prompt | string | | No |
|
||||
| suggested_questions | | | No |
|
||||
| suggested_questions_after_answer | | | No |
|
||||
| user_input_form | | | No |
|
||||
|
||||
#### WebSiteResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| chat_color_theme | string | | No |
|
||||
| chat_color_theme_inverted | boolean | | Yes |
|
||||
| copyright | string | | No |
|
||||
| custom_disclaimer | string | | No |
|
||||
| default_language | string | | No |
|
||||
| description | string | | No |
|
||||
| icon | string | | No |
|
||||
| icon_background | string | | No |
|
||||
| icon_type | string | | No |
|
||||
| icon_url | string | | Yes |
|
||||
| privacy_policy | string | | No |
|
||||
| prompt_public | boolean | | No |
|
||||
| show_workflow_steps | boolean | | No |
|
||||
| title | string | | Yes |
|
||||
| use_icon_as_answer_icon | boolean | | No |
|
||||
|
||||
#### WorkflowRunPayload
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|
||||
@ -173,7 +173,7 @@ class FileService:
|
||||
|
||||
return upload_file
|
||||
|
||||
def get_file_preview(self, file_id: str, tenant_id: str):
|
||||
def get_file_preview(self, file_id: str, tenant_id: str) -> str:
|
||||
"""
|
||||
Return a short text preview extracted from a document file.
|
||||
"""
|
||||
@ -191,9 +191,7 @@ class FileService:
|
||||
raise UnsupportedFileTypeError()
|
||||
|
||||
text = ExtractProcessor.load_from_upload_file(upload_file, return_text=True)
|
||||
text = text[0:PREVIEW_WORDS_LIMIT] if text else ""
|
||||
|
||||
return text
|
||||
return text[0:PREVIEW_WORDS_LIMIT] if text else ""
|
||||
|
||||
def get_image_preview(self, file_id: str, timestamp: str, nonce: str, sign: str):
|
||||
result = file_helpers.verify_image_signature(
|
||||
|
||||
@ -2,21 +2,22 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.web.site import AppSiteApi, AppSiteInfo
|
||||
from controllers.web.site import AppSiteApi, WebAppSiteResponse, WebModelConfigResponse
|
||||
from models import Tenant, TenantStatus
|
||||
from models.model import App, AppMode, CustomizeTokenStrategy, Site
|
||||
from models.account import TenantCustomConfigDict
|
||||
from models.model import App, AppMode, AppModelConfig, CustomizeTokenStrategy, EndUser, Site
|
||||
from services.feature_service import FeatureModel
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app(flask_app_with_containers) -> Flask:
|
||||
def app(flask_app_with_containers: Flask) -> Flask:
|
||||
return flask_app_with_containers
|
||||
|
||||
|
||||
@ -41,7 +42,23 @@ def _create_app(db_session: Session, tenant_id: str, *, enable_site: bool = True
|
||||
|
||||
|
||||
def _create_site(db_session: Session, app_id: str) -> Site:
|
||||
site = Site(
|
||||
site = _site_model(app_id=app_id)
|
||||
db_session.add(site)
|
||||
db_session.commit()
|
||||
return site
|
||||
|
||||
|
||||
def _end_user(tenant_id: str, app_id: str) -> EndUser:
|
||||
return EndUser(
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_id,
|
||||
type="browser",
|
||||
session_id=f"session-{app_id}",
|
||||
)
|
||||
|
||||
|
||||
def _site_model(*, app_id: str) -> Site:
|
||||
return Site(
|
||||
app_id=app_id,
|
||||
title="Site",
|
||||
icon_type="emoji",
|
||||
@ -51,31 +68,30 @@ def _create_site(db_session: Session, app_id: str) -> Site:
|
||||
default_language="en",
|
||||
chat_color_theme="light",
|
||||
chat_color_theme_inverted=False,
|
||||
custom_disclaimer="",
|
||||
customize_token_strategy=CustomizeTokenStrategy.NOT_ALLOW,
|
||||
code=f"code-{app_id[-6:]}",
|
||||
prompt_public=False,
|
||||
show_workflow_steps=True,
|
||||
use_icon_as_answer_icon=False,
|
||||
)
|
||||
db_session.add(site)
|
||||
db_session.commit()
|
||||
return site
|
||||
|
||||
|
||||
class TestAppSiteApi:
|
||||
@patch("controllers.web.site.FeatureService.get_features")
|
||||
def test_happy_path(self, mock_features, app: Flask, db_session_with_containers: Session) -> None:
|
||||
def test_happy_path(self, mock_features: MagicMock, app: Flask, db_session_with_containers: Session) -> None:
|
||||
app.config["RESTX_MASK_HEADER"] = "X-Fields"
|
||||
tenant = _create_tenant(db_session_with_containers)
|
||||
app_model = _create_app(db_session_with_containers, tenant.id)
|
||||
_create_site(db_session_with_containers, app_model.id)
|
||||
end_user = SimpleNamespace(id="eu-1")
|
||||
mock_features.return_value = SimpleNamespace(can_replace_logo=False)
|
||||
end_user = _end_user(tenant.id, app_model.id)
|
||||
mock_features.return_value = FeatureModel(can_replace_logo=False)
|
||||
|
||||
with app.test_request_context("/site"):
|
||||
result = AppSiteApi().get(app_model, end_user)
|
||||
|
||||
assert result["app_id"] == app_model.id
|
||||
assert result["end_user_id"] == end_user.id
|
||||
assert result["plan"] == "basic"
|
||||
assert result["enable_site"] is True
|
||||
|
||||
@ -83,51 +99,139 @@ class TestAppSiteApi:
|
||||
app.config["RESTX_MASK_HEADER"] = "X-Fields"
|
||||
tenant = _create_tenant(db_session_with_containers)
|
||||
app_model = _create_app(db_session_with_containers, tenant.id)
|
||||
end_user = SimpleNamespace(id="eu-1")
|
||||
end_user = _end_user(tenant.id, app_model.id)
|
||||
|
||||
with app.test_request_context("/site"):
|
||||
with pytest.raises(Forbidden):
|
||||
AppSiteApi().get(app_model, end_user)
|
||||
|
||||
@patch("controllers.web.site.FeatureService.get_features")
|
||||
def test_archived_tenant_raises_forbidden(
|
||||
self, mock_features, app: Flask, db_session_with_containers: Session
|
||||
) -> None:
|
||||
def test_archived_tenant_raises_forbidden(self, app: Flask, db_session_with_containers: Session) -> None:
|
||||
app.config["RESTX_MASK_HEADER"] = "X-Fields"
|
||||
tenant = _create_tenant(db_session_with_containers, status=TenantStatus.ARCHIVE)
|
||||
app_model = _create_app(db_session_with_containers, tenant.id)
|
||||
_create_site(db_session_with_containers, app_model.id)
|
||||
end_user = SimpleNamespace(id="eu-1")
|
||||
mock_features.return_value = SimpleNamespace(can_replace_logo=False)
|
||||
end_user = _end_user(tenant.id, app_model.id)
|
||||
|
||||
with app.test_request_context("/site"):
|
||||
with pytest.raises(Forbidden):
|
||||
AppSiteApi().get(app_model, end_user)
|
||||
|
||||
|
||||
class TestAppSiteInfo:
|
||||
def _tenant_model(*, plan: str = "basic", custom_config: TenantCustomConfigDict | None = None) -> Tenant:
|
||||
tenant = Tenant(name="test-tenant", plan=plan)
|
||||
tenant.custom_config_dict = custom_config or {}
|
||||
return tenant
|
||||
|
||||
|
||||
def _app_model(*, tenant: Tenant, enable_site: bool = True) -> App:
|
||||
app_model = App(
|
||||
tenant_id=tenant.id,
|
||||
mode=AppMode.CHAT,
|
||||
name="test-app",
|
||||
enable_site=enable_site,
|
||||
enable_api=True,
|
||||
)
|
||||
app_model.id = "app-test"
|
||||
return app_model
|
||||
|
||||
|
||||
class TestWebAppSiteResponse:
|
||||
def test_basic_fields(self) -> None:
|
||||
tenant = SimpleNamespace(id="tenant-1", plan="basic", custom_config_dict={})
|
||||
site_obj = SimpleNamespace()
|
||||
info = AppSiteInfo(tenant, SimpleNamespace(id="app-1", enable_site=True), site_obj, "eu-1", False)
|
||||
|
||||
assert info.app_id == "app-1"
|
||||
assert info.end_user_id == "eu-1"
|
||||
assert info.enable_site is True
|
||||
assert info.plan == "basic"
|
||||
assert info.can_replace_logo is False
|
||||
assert info.model_config is None
|
||||
|
||||
@patch("controllers.web.site.dify_config", SimpleNamespace(FILES_URL="https://files.example.com"))
|
||||
def test_can_replace_logo_sets_custom_config(self) -> None:
|
||||
tenant = SimpleNamespace(
|
||||
id="tenant-1",
|
||||
plan="pro",
|
||||
custom_config_dict={"remove_webapp_brand": True, "replace_webapp_logo": True},
|
||||
tenant = _tenant_model()
|
||||
app_model = _app_model(tenant=tenant)
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
site=_site_model(app_id=app_model.id),
|
||||
end_user_id="eu-1",
|
||||
can_replace_logo=False,
|
||||
)
|
||||
site_obj = SimpleNamespace()
|
||||
info = AppSiteInfo(tenant, SimpleNamespace(id="app-1", enable_site=True), site_obj, "eu-1", True)
|
||||
|
||||
assert info.can_replace_logo is True
|
||||
assert info.custom_config["remove_webapp_brand"] is True
|
||||
assert "webapp-logo" in info.custom_config["replace_webapp_logo"]
|
||||
assert response.app_id == app_model.id
|
||||
assert response.end_user_id == "eu-1"
|
||||
assert response.enable_site is True
|
||||
assert response.plan == "basic"
|
||||
assert response.can_replace_logo is False
|
||||
assert response.model_config_ is None
|
||||
assert response.custom_config is None
|
||||
assert response.site.custom_disclaimer == ""
|
||||
|
||||
def test_nullable_site_fields_preserve_none(self) -> None:
|
||||
tenant = _tenant_model()
|
||||
app_model = _app_model(tenant=tenant)
|
||||
site = _site_model(app_id=app_model.id)
|
||||
site.chat_color_theme = None
|
||||
site.icon_type = None
|
||||
site.icon = None
|
||||
site.icon_background = None
|
||||
site.description = None
|
||||
site.copyright = None
|
||||
site.privacy_policy = None
|
||||
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
site=site,
|
||||
end_user_id=None,
|
||||
can_replace_logo=False,
|
||||
)
|
||||
|
||||
dumped = response.model_dump(mode="json")
|
||||
assert dumped["end_user_id"] is None
|
||||
assert dumped["site"]["chat_color_theme"] is None
|
||||
assert dumped["site"]["icon_type"] is None
|
||||
assert dumped["site"]["icon"] is None
|
||||
assert dumped["site"]["icon_background"] is None
|
||||
assert dumped["site"]["description"] is None
|
||||
assert dumped["site"]["copyright"] is None
|
||||
assert dumped["site"]["privacy_policy"] is None
|
||||
assert dumped["site"]["custom_disclaimer"] == ""
|
||||
|
||||
@patch("controllers.web.site.dify_config.FILES_URL", "https://files.example.com")
|
||||
def test_can_replace_logo_sets_custom_config(self) -> None:
|
||||
tenant = _tenant_model(
|
||||
plan="pro",
|
||||
custom_config={"remove_webapp_brand": True, "replace_webapp_logo": "enabled"},
|
||||
)
|
||||
app_model = _app_model(tenant=tenant)
|
||||
response = WebAppSiteResponse.from_app_site(
|
||||
tenant=tenant,
|
||||
app_model=app_model,
|
||||
site=_site_model(app_id=app_model.id),
|
||||
end_user_id="eu-1",
|
||||
can_replace_logo=True,
|
||||
)
|
||||
|
||||
assert response.can_replace_logo is True
|
||||
assert response.custom_config is not None
|
||||
assert response.custom_config.remove_webapp_brand is True
|
||||
assert response.custom_config.replace_webapp_logo is not None
|
||||
assert "webapp-logo" in response.custom_config.replace_webapp_logo
|
||||
|
||||
|
||||
class TestWebModelConfigResponse:
|
||||
def test_serializes_internal_model_config_properties_to_public_keys(self) -> None:
|
||||
model_config = AppModelConfig(
|
||||
app_id="app-test",
|
||||
opening_statement="Hello",
|
||||
suggested_questions='["Question?"]',
|
||||
suggested_questions_after_answer='{"enabled": true}',
|
||||
more_like_this='{"enabled": false}',
|
||||
model='{"provider": "openai", "name": "gpt-4o", "mode": "chat"}',
|
||||
user_input_form='[{"text-input": {"label": "Name", "variable": "name", "required": true}}]',
|
||||
pre_prompt="System prompt",
|
||||
created_by="account-1",
|
||||
updated_by="account-1",
|
||||
)
|
||||
|
||||
dumped = WebModelConfigResponse.model_validate(model_config, from_attributes=True).model_dump(mode="json")
|
||||
|
||||
assert dumped == {
|
||||
"opening_statement": "Hello",
|
||||
"suggested_questions": ["Question?"],
|
||||
"suggested_questions_after_answer": {"enabled": True},
|
||||
"more_like_this": {"enabled": False},
|
||||
"model": {"provider": "openai", "name": "gpt-4o", "mode": "chat"},
|
||||
"user_input_form": [{"text-input": {"label": "Name", "variable": "name", "required": True}}],
|
||||
"pre_prompt": "System prompt",
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
@ -112,7 +111,7 @@ def test_get_form_includes_site(monkeypatch: pytest.MonkeyPatch, app: Flask):
|
||||
chat_color_theme_inverted=False,
|
||||
copyright=None,
|
||||
privacy_policy=None,
|
||||
custom_disclaimer=None,
|
||||
custom_disclaimer="",
|
||||
prompt_public=False,
|
||||
show_workflow_steps=True,
|
||||
use_icon_as_answer_icon=False,
|
||||
@ -138,7 +137,7 @@ def test_get_form_includes_site(monkeypatch: pytest.MonkeyPatch, app: Flask):
|
||||
with app.test_request_context("/api/form/human_input/token-1", method="GET"):
|
||||
response = HumanInputFormApi().get("token-1")
|
||||
|
||||
body = json.loads(response.get_data(as_text=True))
|
||||
body = response
|
||||
assert set(body.keys()) == {
|
||||
"site",
|
||||
"form_content",
|
||||
@ -167,7 +166,7 @@ def test_get_form_includes_site(monkeypatch: pytest.MonkeyPatch, app: Flask):
|
||||
"description": "desc",
|
||||
"copyright": None,
|
||||
"privacy_policy": None,
|
||||
"custom_disclaimer": None,
|
||||
"custom_disclaimer": "",
|
||||
"default_language": "en",
|
||||
"prompt_public": False,
|
||||
"show_workflow_steps": True,
|
||||
@ -256,7 +255,7 @@ def test_get_form_uses_runtime_select_options(monkeypatch: pytest.MonkeyPatch, a
|
||||
chat_color_theme_inverted=False,
|
||||
copyright=None,
|
||||
privacy_policy=None,
|
||||
custom_disclaimer=None,
|
||||
custom_disclaimer="",
|
||||
prompt_public=False,
|
||||
show_workflow_steps=True,
|
||||
use_icon_as_answer_icon=False,
|
||||
@ -277,7 +276,7 @@ def test_get_form_uses_runtime_select_options(monkeypatch: pytest.MonkeyPatch, a
|
||||
with app.test_request_context("/api/form/human_input/token-1", method="GET"):
|
||||
response = HumanInputFormApi().get("token-1")
|
||||
|
||||
body = json.loads(response.get_data(as_text=True))
|
||||
body = response
|
||||
assert body["inputs"] == [input_config.model_dump(mode="json") for input_config in runtime_inputs]
|
||||
service_mock.resolve_form_inputs.assert_called_once_with(form)
|
||||
|
||||
@ -380,7 +379,7 @@ def test_get_form_allows_backstage_token(monkeypatch: pytest.MonkeyPatch, app: F
|
||||
chat_color_theme_inverted=False,
|
||||
copyright=None,
|
||||
privacy_policy=None,
|
||||
custom_disclaimer=None,
|
||||
custom_disclaimer="",
|
||||
prompt_public=False,
|
||||
show_workflow_steps=True,
|
||||
use_icon_as_answer_icon=False,
|
||||
@ -403,7 +402,7 @@ def test_get_form_allows_backstage_token(monkeypatch: pytest.MonkeyPatch, app: F
|
||||
with app.test_request_context("/api/form/human_input/token-1", method="GET"):
|
||||
response = HumanInputFormApi().get("token-1")
|
||||
|
||||
body = json.loads(response.get_data(as_text=True))
|
||||
body = response
|
||||
assert set(body.keys()) == {
|
||||
"site",
|
||||
"form_content",
|
||||
@ -432,7 +431,7 @@ def test_get_form_allows_backstage_token(monkeypatch: pytest.MonkeyPatch, app: F
|
||||
"description": "desc",
|
||||
"copyright": None,
|
||||
"privacy_policy": None,
|
||||
"custom_disclaimer": None,
|
||||
"custom_disclaimer": "",
|
||||
"default_language": "en",
|
||||
"prompt_public": False,
|
||||
"show_workflow_steps": True,
|
||||
|
||||
@ -269,6 +269,7 @@ export type MessageDetailResponse = {
|
||||
agent_thoughts?: Array<AgentThought>
|
||||
annotation?: ConversationAnnotation | null
|
||||
annotation_hit_history?: ConversationAnnotationHitHistory | null
|
||||
answer: string
|
||||
answer_tokens?: number | null
|
||||
conversation_id: string
|
||||
created_at?: number | null
|
||||
@ -284,12 +285,11 @@ export type MessageDetailResponse = {
|
||||
}
|
||||
message?: JsonValue | null
|
||||
message_files?: Array<MessageFile>
|
||||
message_metadata_dict?: JsonValue | null
|
||||
message_tokens?: number | null
|
||||
metadata?: JsonValue | null
|
||||
parent_message_id?: string | null
|
||||
provider_response_latency?: number | null
|
||||
query: string
|
||||
re_sign_file_url_answer: string
|
||||
status: string
|
||||
workflow_run_id?: string | null
|
||||
}
|
||||
@ -723,7 +723,6 @@ export type AgentThought = {
|
||||
created_at?: number | null
|
||||
files: Array<string>
|
||||
id: string
|
||||
message_chain_id?: string | null
|
||||
message_id: string
|
||||
observation?: string | null
|
||||
position: number
|
||||
@ -743,8 +742,8 @@ export type ConversationAnnotation = {
|
||||
|
||||
export type ConversationAnnotationHitHistory = {
|
||||
annotation_create_account?: SimpleAccount | null
|
||||
annotation_id: string
|
||||
created_at?: number | null
|
||||
id: string
|
||||
}
|
||||
|
||||
export type HumanInputContent = {
|
||||
|
||||
@ -570,7 +570,6 @@ export const zAgentThought = z.object({
|
||||
created_at: z.int().nullish(),
|
||||
files: z.array(z.string()),
|
||||
id: z.string(),
|
||||
message_chain_id: z.string().nullish(),
|
||||
message_id: z.string(),
|
||||
observation: z.string().nullish(),
|
||||
position: z.int(),
|
||||
@ -1056,8 +1055,8 @@ export const zConversationAnnotation = z.object({
|
||||
*/
|
||||
export const zConversationAnnotationHitHistory = z.object({
|
||||
annotation_create_account: zSimpleAccount.nullish(),
|
||||
annotation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
id: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -2035,6 +2034,7 @@ export const zMessageDetailResponse = z.object({
|
||||
agent_thoughts: z.array(zAgentThought).optional(),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
|
||||
answer: z.string(),
|
||||
answer_tokens: z.int().nullish(),
|
||||
conversation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
@ -2048,12 +2048,11 @@ export const zMessageDetailResponse = z.object({
|
||||
inputs: z.record(z.string(), zJsonValue),
|
||||
message: zJsonValue.nullish(),
|
||||
message_files: z.array(zMessageFile).optional(),
|
||||
message_metadata_dict: zJsonValue.nullish(),
|
||||
message_tokens: z.int().nullish(),
|
||||
metadata: zJsonValue.nullish(),
|
||||
parent_message_id: z.string().nullish(),
|
||||
provider_response_latency: z.number().nullish(),
|
||||
query: z.string(),
|
||||
re_sign_file_url_answer: z.string(),
|
||||
status: z.string(),
|
||||
workflow_run_id: z.string().nullish(),
|
||||
})
|
||||
|
||||
@ -472,6 +472,7 @@ export type MessageDetailResponse = {
|
||||
agent_thoughts?: Array<AgentThought>
|
||||
annotation?: ConversationAnnotation | null
|
||||
annotation_hit_history?: ConversationAnnotationHitHistory | null
|
||||
answer: string
|
||||
answer_tokens?: number | null
|
||||
conversation_id: string
|
||||
created_at?: number | null
|
||||
@ -487,12 +488,11 @@ export type MessageDetailResponse = {
|
||||
}
|
||||
message?: JsonValue | null
|
||||
message_files?: Array<MessageFile>
|
||||
message_metadata_dict?: JsonValue | null
|
||||
message_tokens?: number | null
|
||||
metadata?: JsonValue | null
|
||||
parent_message_id?: string | null
|
||||
provider_response_latency?: number | null
|
||||
query: string
|
||||
re_sign_file_url_answer: string
|
||||
status: string
|
||||
workflow_run_id?: string | null
|
||||
}
|
||||
@ -1498,7 +1498,6 @@ export type AgentThought = {
|
||||
created_at?: number | null
|
||||
files: Array<string>
|
||||
id: string
|
||||
message_chain_id?: string | null
|
||||
message_id: string
|
||||
observation?: string | null
|
||||
position: number
|
||||
@ -1518,8 +1517,8 @@ export type ConversationAnnotation = {
|
||||
|
||||
export type ConversationAnnotationHitHistory = {
|
||||
annotation_create_account?: SimpleAccount | null
|
||||
annotation_id: string
|
||||
created_at?: number | null
|
||||
id: string
|
||||
}
|
||||
|
||||
export type HumanInputContent = {
|
||||
|
||||
@ -1150,7 +1150,6 @@ export const zAgentThought = z.object({
|
||||
created_at: z.int().nullish(),
|
||||
files: z.array(z.string()),
|
||||
id: z.string(),
|
||||
message_chain_id: z.string().nullish(),
|
||||
message_id: z.string(),
|
||||
observation: z.string().nullish(),
|
||||
position: z.int(),
|
||||
@ -1371,8 +1370,8 @@ export const zConversationAnnotation = z.object({
|
||||
*/
|
||||
export const zConversationAnnotationHitHistory = z.object({
|
||||
annotation_create_account: zSimpleAccount.nullish(),
|
||||
annotation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
id: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -3455,6 +3454,7 @@ export const zMessageDetailResponse = z.object({
|
||||
agent_thoughts: z.array(zAgentThought).optional(),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
|
||||
answer: z.string(),
|
||||
answer_tokens: z.int().nullish(),
|
||||
conversation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
@ -3468,12 +3468,11 @@ export const zMessageDetailResponse = z.object({
|
||||
inputs: z.record(z.string(), zJsonValue),
|
||||
message: zJsonValue.nullish(),
|
||||
message_files: z.array(zMessageFile).optional(),
|
||||
message_metadata_dict: zJsonValue.nullish(),
|
||||
message_tokens: z.int().nullish(),
|
||||
metadata: zJsonValue.nullish(),
|
||||
parent_message_id: z.string().nullish(),
|
||||
provider_response_latency: z.number().nullish(),
|
||||
query: z.string(),
|
||||
re_sign_file_url_answer: z.string(),
|
||||
status: z.string(),
|
||||
workflow_run_id: z.string().nullish(),
|
||||
})
|
||||
|
||||
@ -9,11 +9,11 @@ export type AllowedExtensionsResponse = {
|
||||
}
|
||||
|
||||
export type UploadConfig = {
|
||||
attachment_image_file_size_limit?: number | null
|
||||
attachment_image_file_size_limit: number
|
||||
audio_file_size_limit: number
|
||||
batch_count_limit: number
|
||||
file_size_limit: number
|
||||
file_upload_limit?: number | null
|
||||
file_upload_limit: number
|
||||
image_file_batch_limit: number
|
||||
image_file_size_limit: number
|
||||
single_chunk_attachment_limit: number
|
||||
|
||||
@ -13,11 +13,11 @@ export const zAllowedExtensionsResponse = z.object({
|
||||
* UploadConfig
|
||||
*/
|
||||
export const zUploadConfig = z.object({
|
||||
attachment_image_file_size_limit: z.int().nullish(),
|
||||
attachment_image_file_size_limit: z.int(),
|
||||
audio_file_size_limit: z.int(),
|
||||
batch_count_limit: z.int(),
|
||||
file_size_limit: z.int(),
|
||||
file_upload_limit: z.int().nullish(),
|
||||
file_upload_limit: z.int(),
|
||||
image_file_batch_limit: z.int(),
|
||||
image_file_size_limit: z.int(),
|
||||
single_chunk_attachment_limit: z.int(),
|
||||
|
||||
@ -246,7 +246,6 @@ export type AgentThought = {
|
||||
created_at?: number | null
|
||||
files: Array<string>
|
||||
id: string
|
||||
message_chain_id?: string | null
|
||||
message_id: string
|
||||
observation?: string | null
|
||||
position: number
|
||||
|
||||
@ -266,7 +266,6 @@ export const zAgentThought = z.object({
|
||||
created_at: z.int().nullish(),
|
||||
files: z.array(z.string()),
|
||||
id: z.string(),
|
||||
message_chain_id: z.string().nullish(),
|
||||
message_id: z.string(),
|
||||
observation: z.string().nullish(),
|
||||
position: z.int(),
|
||||
|
||||
@ -14,9 +14,6 @@ import {
|
||||
zGetFormHumanInputByFormTokenResponse,
|
||||
zGetLoginStatusQuery,
|
||||
zGetLoginStatusResponse,
|
||||
zGetMessagesByMessageIdMoreLikeThisPath,
|
||||
zGetMessagesByMessageIdMoreLikeThisQuery,
|
||||
zGetMessagesByMessageIdMoreLikeThisResponse,
|
||||
zGetMessagesByMessageIdSuggestedQuestionsPath,
|
||||
zGetMessagesByMessageIdSuggestedQuestionsResponse,
|
||||
zGetMessagesQuery,
|
||||
@ -42,14 +39,10 @@ import {
|
||||
zPatchConversationsByCIdUnpinPath,
|
||||
zPatchConversationsByCIdUnpinResponse,
|
||||
zPostAudioToTextResponse,
|
||||
zPostChatMessagesBody,
|
||||
zPostChatMessagesByTaskIdStopPath,
|
||||
zPostChatMessagesByTaskIdStopResponse,
|
||||
zPostChatMessagesResponse,
|
||||
zPostCompletionMessagesBody,
|
||||
zPostCompletionMessagesByTaskIdStopPath,
|
||||
zPostCompletionMessagesByTaskIdStopResponse,
|
||||
zPostCompletionMessagesResponse,
|
||||
zPostConversationsByCIdNameBody,
|
||||
zPostConversationsByCIdNamePath,
|
||||
zPostConversationsByCIdNameQuery,
|
||||
@ -83,10 +76,6 @@ import {
|
||||
zPostSavedMessagesBody,
|
||||
zPostSavedMessagesQuery,
|
||||
zPostSavedMessagesResponse,
|
||||
zPostTextToAudioBody,
|
||||
zPostTextToAudioResponse,
|
||||
zPostWorkflowsRunBody,
|
||||
zPostWorkflowsRunResponse,
|
||||
zPostWorkflowsTasksByTaskIdStopPath,
|
||||
zPostWorkflowsTasksByTaskIdStopResponse,
|
||||
} from './zod.gen'
|
||||
@ -135,30 +124,14 @@ export const byTaskId = {
|
||||
stop,
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a chat message for conversational applications.
|
||||
*/
|
||||
export const post3 = oc
|
||||
.route({
|
||||
description: 'Create a chat message for conversational applications.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postChatMessages',
|
||||
path: '/chat-messages',
|
||||
tags: ['web'],
|
||||
})
|
||||
.input(z.object({ body: zPostChatMessagesBody }))
|
||||
.output(zPostChatMessagesResponse)
|
||||
|
||||
export const chatMessages = {
|
||||
post: post3,
|
||||
byTaskId,
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running completion message task.
|
||||
*/
|
||||
export const post4 = oc
|
||||
export const post3 = oc
|
||||
.route({
|
||||
description: 'Stop a running completion message task.',
|
||||
inputStructure: 'detailed',
|
||||
@ -171,37 +144,21 @@ export const post4 = oc
|
||||
.output(zPostCompletionMessagesByTaskIdStopResponse)
|
||||
|
||||
export const stop2 = {
|
||||
post: post4,
|
||||
post: post3,
|
||||
}
|
||||
|
||||
export const byTaskId2 = {
|
||||
stop: stop2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a completion message for text generation applications.
|
||||
*/
|
||||
export const post5 = oc
|
||||
.route({
|
||||
description: 'Create a completion message for text generation applications.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postCompletionMessages',
|
||||
path: '/completion-messages',
|
||||
tags: ['web'],
|
||||
})
|
||||
.input(z.object({ body: zPostCompletionMessagesBody }))
|
||||
.output(zPostCompletionMessagesResponse)
|
||||
|
||||
export const completionMessages = {
|
||||
post: post5,
|
||||
byTaskId: byTaskId2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a specific conversation with a custom name or auto-generate one.
|
||||
*/
|
||||
export const post6 = oc
|
||||
export const post4 = oc
|
||||
.route({
|
||||
description: 'Rename a specific conversation with a custom name or auto-generate one.',
|
||||
inputStructure: 'detailed',
|
||||
@ -220,7 +177,7 @@ export const post6 = oc
|
||||
.output(zPostConversationsByCIdNameResponse)
|
||||
|
||||
export const name = {
|
||||
post: post6,
|
||||
post: post4,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,7 +264,7 @@ export const conversations = {
|
||||
/**
|
||||
* Verify email code and complete login
|
||||
*/
|
||||
export const post7 = oc
|
||||
export const post5 = oc
|
||||
.route({
|
||||
description: 'Verify email code and complete login',
|
||||
inputStructure: 'detailed',
|
||||
@ -320,13 +277,13 @@ export const post7 = oc
|
||||
.output(zPostEmailCodeLoginValidityResponse)
|
||||
|
||||
export const validity = {
|
||||
post: post7,
|
||||
post: post5,
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email verification code for login
|
||||
*/
|
||||
export const post8 = oc
|
||||
export const post6 = oc
|
||||
.route({
|
||||
description: 'Send email verification code for login',
|
||||
inputStructure: 'detailed',
|
||||
@ -339,7 +296,7 @@ export const post8 = oc
|
||||
.output(zPostEmailCodeLoginResponse)
|
||||
|
||||
export const emailCodeLogin = {
|
||||
post: post8,
|
||||
post: post6,
|
||||
validity,
|
||||
}
|
||||
|
||||
@ -369,7 +326,7 @@ export const emailCodeLogin = {
|
||||
* FileTooLargeError: File exceeds size limit
|
||||
* UnsupportedFileTypeError: File type not supported
|
||||
*/
|
||||
export const post9 = oc
|
||||
export const post7 = oc
|
||||
.route({
|
||||
description:
|
||||
'Upload a file for use in web applications\nAccepts file uploads for use within web applications, supporting\nmultiple file types with automatic validation and storage.\n\nArgs:\n app_model: The associated application model\n end_user: The end user uploading the file\n\nForm Parameters:\n file: The file to upload (required)\n source: Optional source type (datasets or None)\n\nReturns:\n dict: File information including ID, URL, and metadata\n int: HTTP status code 201 for success\n\nRaises:\n NoFileUploadedError: No file provided in request\n TooManyFilesError: Multiple files provided (only one allowed)\n FilenameNotExistsError: File has no filename\n FileTooLargeError: File exceeds size limit\n UnsupportedFileTypeError: File type not supported',
|
||||
@ -384,7 +341,7 @@ export const post9 = oc
|
||||
.output(zPostFilesUploadResponse)
|
||||
|
||||
export const upload = {
|
||||
post: post9,
|
||||
post: post7,
|
||||
}
|
||||
|
||||
export const files = {
|
||||
@ -394,7 +351,7 @@ export const files = {
|
||||
/**
|
||||
* Reset user password with verification token
|
||||
*/
|
||||
export const post10 = oc
|
||||
export const post8 = oc
|
||||
.route({
|
||||
description: 'Reset user password with verification token',
|
||||
inputStructure: 'detailed',
|
||||
@ -407,13 +364,13 @@ export const post10 = oc
|
||||
.output(zPostForgotPasswordResetsResponse)
|
||||
|
||||
export const resets = {
|
||||
post: post10,
|
||||
post: post8,
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify password reset token validity
|
||||
*/
|
||||
export const post11 = oc
|
||||
export const post9 = oc
|
||||
.route({
|
||||
description: 'Verify password reset token validity',
|
||||
inputStructure: 'detailed',
|
||||
@ -426,13 +383,13 @@ export const post11 = oc
|
||||
.output(zPostForgotPasswordValidityResponse)
|
||||
|
||||
export const validity2 = {
|
||||
post: post11,
|
||||
post: post9,
|
||||
}
|
||||
|
||||
/**
|
||||
* Send password reset email
|
||||
*/
|
||||
export const post12 = oc
|
||||
export const post10 = oc
|
||||
.route({
|
||||
description: 'Send password reset email',
|
||||
inputStructure: 'detailed',
|
||||
@ -445,7 +402,7 @@ export const post12 = oc
|
||||
.output(zPostForgotPasswordResponse)
|
||||
|
||||
export const forgotPassword = {
|
||||
post: post12,
|
||||
post: post10,
|
||||
resets,
|
||||
validity: validity2,
|
||||
}
|
||||
@ -453,11 +410,13 @@ export const forgotPassword = {
|
||||
/**
|
||||
* Issue an upload token for a human input form
|
||||
*
|
||||
* Issue an upload token for an active human input form
|
||||
* POST /api/form/human_input/<form_token>/upload-token
|
||||
*/
|
||||
export const post13 = oc
|
||||
export const post11 = oc
|
||||
.route({
|
||||
description: 'POST /api/form/human_input/<form_token>/upload-token',
|
||||
description:
|
||||
'Issue an upload token for an active human input form\nPOST /api/form/human_input/<form_token>/upload-token',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postFormHumanInputByFormTokenUploadToken',
|
||||
@ -469,17 +428,19 @@ export const post13 = oc
|
||||
.output(zPostFormHumanInputByFormTokenUploadTokenResponse)
|
||||
|
||||
export const uploadToken = {
|
||||
post: post13,
|
||||
post: post11,
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human input form definition by token
|
||||
*
|
||||
* Get a human input form definition by token
|
||||
* GET /api/form/human_input/<form_token>
|
||||
*/
|
||||
export const get2 = oc
|
||||
.route({
|
||||
description: 'GET /api/form/human_input/<form_token>',
|
||||
description:
|
||||
'Get a human input form definition by token\nGET /api/form/human_input/<form_token>',
|
||||
inputStructure: 'detailed',
|
||||
method: 'GET',
|
||||
operationId: 'getFormHumanInputByFormToken',
|
||||
@ -493,6 +454,7 @@ export const get2 = oc
|
||||
/**
|
||||
* Submit human input form by token
|
||||
*
|
||||
* Submit a human input form by token
|
||||
* POST /api/form/human_input/<form_token>
|
||||
*
|
||||
* Request body:
|
||||
@ -503,10 +465,10 @@ export const get2 = oc
|
||||
* "action": "Approve"
|
||||
* }
|
||||
*/
|
||||
export const post14 = oc
|
||||
export const post12 = oc
|
||||
.route({
|
||||
description:
|
||||
'POST /api/form/human_input/<form_token>\n\nRequest body:\n{\n "inputs": {\n "content": "User input content"\n },\n "action": "Approve"\n}',
|
||||
'Submit a human input form by token\nPOST /api/form/human_input/<form_token>\n\nRequest body:\n{\n "inputs": {\n "content": "User input content"\n },\n "action": "Approve"\n}',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postFormHumanInputByFormToken',
|
||||
@ -524,7 +486,7 @@ export const post14 = oc
|
||||
|
||||
export const byFormToken = {
|
||||
get: get2,
|
||||
post: post14,
|
||||
post: post12,
|
||||
uploadToken,
|
||||
}
|
||||
|
||||
@ -539,7 +501,7 @@ export const form = {
|
||||
/**
|
||||
* Upload one local file or remote URL file for a HITL human input form
|
||||
*/
|
||||
export const post15 = oc
|
||||
export const post13 = oc
|
||||
.route({
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
@ -552,7 +514,7 @@ export const post15 = oc
|
||||
.output(zPostHumanInputFormsFilesResponse)
|
||||
|
||||
export const files2 = {
|
||||
post: post15,
|
||||
post: post13,
|
||||
}
|
||||
|
||||
export const humanInputForms = {
|
||||
@ -583,7 +545,7 @@ export const status = {
|
||||
*
|
||||
* Authenticate user for web application access
|
||||
*/
|
||||
export const post16 = oc
|
||||
export const post14 = oc
|
||||
.route({
|
||||
description: 'Authenticate user for web application access',
|
||||
inputStructure: 'detailed',
|
||||
@ -597,14 +559,14 @@ export const post16 = oc
|
||||
.output(zPostLoginResponse)
|
||||
|
||||
export const login = {
|
||||
post: post16,
|
||||
post: post14,
|
||||
status,
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user from web application
|
||||
*/
|
||||
export const post17 = oc
|
||||
export const post15 = oc
|
||||
.route({
|
||||
description: 'Logout user from web application',
|
||||
inputStructure: 'detailed',
|
||||
@ -616,13 +578,13 @@ export const post17 = oc
|
||||
.output(zPostLogoutResponse)
|
||||
|
||||
export const logout = {
|
||||
post: post17,
|
||||
post: post15,
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit feedback (like/dislike) for a specific message.
|
||||
*/
|
||||
export const post18 = oc
|
||||
export const post16 = oc
|
||||
.route({
|
||||
description: 'Submit feedback (like/dislike) for a specific message.',
|
||||
inputStructure: 'detailed',
|
||||
@ -641,37 +603,13 @@ export const post18 = oc
|
||||
.output(zPostMessagesByMessageIdFeedbacksResponse)
|
||||
|
||||
export const feedbacks = {
|
||||
post: post18,
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new completion similar to an existing message (completion apps only).
|
||||
*/
|
||||
export const get4 = oc
|
||||
.route({
|
||||
description: 'Generate a new completion similar to an existing message (completion apps only).',
|
||||
inputStructure: 'detailed',
|
||||
method: 'GET',
|
||||
operationId: 'getMessagesByMessageIdMoreLikeThis',
|
||||
path: '/messages/{message_id}/more-like-this',
|
||||
tags: ['web'],
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
params: zGetMessagesByMessageIdMoreLikeThisPath,
|
||||
query: zGetMessagesByMessageIdMoreLikeThisQuery,
|
||||
}),
|
||||
)
|
||||
.output(zGetMessagesByMessageIdMoreLikeThisResponse)
|
||||
|
||||
export const moreLikeThis = {
|
||||
get: get4,
|
||||
post: post16,
|
||||
}
|
||||
|
||||
/**
|
||||
* Get suggested follow-up questions after a message (chat apps only).
|
||||
*/
|
||||
export const get5 = oc
|
||||
export const get4 = oc
|
||||
.route({
|
||||
description: 'Get suggested follow-up questions after a message (chat apps only).',
|
||||
inputStructure: 'detailed',
|
||||
@ -684,19 +622,18 @@ export const get5 = oc
|
||||
.output(zGetMessagesByMessageIdSuggestedQuestionsResponse)
|
||||
|
||||
export const suggestedQuestions = {
|
||||
get: get5,
|
||||
get: get4,
|
||||
}
|
||||
|
||||
export const byMessageId = {
|
||||
feedbacks,
|
||||
moreLikeThis,
|
||||
suggestedQuestions,
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve paginated list of messages from a conversation in a chat application.
|
||||
*/
|
||||
export const get6 = oc
|
||||
export const get5 = oc
|
||||
.route({
|
||||
description: 'Retrieve paginated list of messages from a conversation in a chat application.',
|
||||
inputStructure: 'detailed',
|
||||
@ -709,7 +646,7 @@ export const get6 = oc
|
||||
.output(zGetMessagesResponse)
|
||||
|
||||
export const messages = {
|
||||
get: get6,
|
||||
get: get5,
|
||||
byMessageId,
|
||||
}
|
||||
|
||||
@ -718,7 +655,7 @@ export const messages = {
|
||||
*
|
||||
* Retrieve the metadata for a specific app.
|
||||
*/
|
||||
export const get7 = oc
|
||||
export const get6 = oc
|
||||
.route({
|
||||
description: 'Retrieve the metadata for a specific app.',
|
||||
inputStructure: 'detailed',
|
||||
@ -731,7 +668,7 @@ export const get7 = oc
|
||||
.output(zGetMetaResponse)
|
||||
|
||||
export const meta = {
|
||||
get: get7,
|
||||
get: get6,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -739,7 +676,7 @@ export const meta = {
|
||||
*
|
||||
* Retrieve the parameters for a specific app.
|
||||
*/
|
||||
export const get8 = oc
|
||||
export const get7 = oc
|
||||
.route({
|
||||
description: 'Retrieve the parameters for a specific app.',
|
||||
inputStructure: 'detailed',
|
||||
@ -752,13 +689,13 @@ export const get8 = oc
|
||||
.output(zGetParametersResponse)
|
||||
|
||||
export const parameters = {
|
||||
get: get8,
|
||||
get: get7,
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authentication passport for web application access
|
||||
*/
|
||||
export const get9 = oc
|
||||
export const get8 = oc
|
||||
.route({
|
||||
description: 'Get authentication passport for web application access',
|
||||
inputStructure: 'detailed',
|
||||
@ -771,7 +708,7 @@ export const get9 = oc
|
||||
.output(zGetPassportResponse)
|
||||
|
||||
export const passport = {
|
||||
get: get9,
|
||||
get: get8,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -797,7 +734,7 @@ export const passport = {
|
||||
* FileTooLargeError: File exceeds size limit
|
||||
* UnsupportedFileTypeError: File type not supported
|
||||
*/
|
||||
export const post19 = oc
|
||||
export const post17 = oc
|
||||
.route({
|
||||
description:
|
||||
'Upload a file from a remote URL\nDownloads a file from the provided remote URL and uploads it\nto the platform storage for use in web applications.\n\nArgs:\n app_model: The associated application model\n end_user: The end user making the request\n\nJSON Parameters:\n url: The remote URL to download the file from (required)\n\nReturns:\n dict: File information including ID, signed URL, and metadata\n int: HTTP status code 201 for success\n\nRaises:\n RemoteFileUploadError: Failed to fetch file from remote URL\n FileTooLargeError: File exceeds size limit\n UnsupportedFileTypeError: File type not supported',
|
||||
@ -813,7 +750,7 @@ export const post19 = oc
|
||||
.output(zPostRemoteFilesUploadResponse)
|
||||
|
||||
export const upload2 = {
|
||||
post: post19,
|
||||
post: post17,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -834,7 +771,7 @@ export const upload2 = {
|
||||
* Raises:
|
||||
* HTTPException: If the remote file cannot be accessed
|
||||
*/
|
||||
export const get10 = oc
|
||||
export const get9 = oc
|
||||
.route({
|
||||
description:
|
||||
'Get information about a remote file\nRetrieves basic information about a file located at a remote URL,\nincluding content type and content length.\n\nArgs:\n app_model: The associated application model\n end_user: The end user making the request\n url: URL-encoded path to the remote file\n\nReturns:\n dict: Remote file information including type and length\n\nRaises:\n HTTPException: If the remote file cannot be accessed',
|
||||
@ -849,7 +786,7 @@ export const get10 = oc
|
||||
.output(zGetRemoteFilesByUrlResponse)
|
||||
|
||||
export const byUrl = {
|
||||
get: get10,
|
||||
get: get9,
|
||||
}
|
||||
|
||||
export const remoteFiles = {
|
||||
@ -880,7 +817,7 @@ export const byMessageId2 = {
|
||||
/**
|
||||
* Retrieve paginated list of saved messages for a completion application.
|
||||
*/
|
||||
export const get11 = oc
|
||||
export const get10 = oc
|
||||
.route({
|
||||
description: 'Retrieve paginated list of saved messages for a completion application.',
|
||||
inputStructure: 'detailed',
|
||||
@ -895,7 +832,7 @@ export const get11 = oc
|
||||
/**
|
||||
* Save a specific message for later reference.
|
||||
*/
|
||||
export const post20 = oc
|
||||
export const post18 = oc
|
||||
.route({
|
||||
description: 'Save a specific message for later reference.',
|
||||
inputStructure: 'detailed',
|
||||
@ -908,8 +845,8 @@ export const post20 = oc
|
||||
.output(zPostSavedMessagesResponse)
|
||||
|
||||
export const savedMessages = {
|
||||
get: get11,
|
||||
post: post20,
|
||||
get: get10,
|
||||
post: post18,
|
||||
byMessageId: byMessageId2,
|
||||
}
|
||||
|
||||
@ -918,7 +855,7 @@ export const savedMessages = {
|
||||
*
|
||||
* Retrieve app site information and configuration.
|
||||
*/
|
||||
export const get12 = oc
|
||||
export const get11 = oc
|
||||
.route({
|
||||
description: 'Retrieve app site information and configuration.',
|
||||
inputStructure: 'detailed',
|
||||
@ -931,7 +868,7 @@ export const get12 = oc
|
||||
.output(zGetSiteResponse)
|
||||
|
||||
export const site = {
|
||||
get: get12,
|
||||
get: get11,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -954,7 +891,7 @@ export const site = {
|
||||
*
|
||||
* Only non-sensitive configuration data should be returned by this endpoint.
|
||||
*/
|
||||
export const get13 = oc
|
||||
export const get12 = oc
|
||||
.route({
|
||||
description:
|
||||
'Get system feature flags and configuration\nReturns the current system feature flags and configuration\nthat control various functionalities across the platform.\n\nReturns:\n dict: System feature configuration object\n\nThis endpoint is akin to the `SystemFeatureApi` endpoint in api/controllers/console/feature.py,\nexcept it is intended for use by the web app, instead of the console dashboard.\n\nNOTE: This endpoint is unauthenticated by design, as it provides system features\ndata required for webapp initialization.\n\nAuthentication would create circular dependency (can\'t authenticate without webapp loading).\n\nOnly non-sensitive configuration data should be returned by this endpoint.',
|
||||
@ -968,35 +905,13 @@ export const get13 = oc
|
||||
.output(zGetSystemFeaturesResponse)
|
||||
|
||||
export const systemFeatures = {
|
||||
get: get13,
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert text to audio
|
||||
*
|
||||
* Convert text to audio using text-to-speech service.
|
||||
*/
|
||||
export const post21 = oc
|
||||
.route({
|
||||
description: 'Convert text to audio using text-to-speech service.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postTextToAudio',
|
||||
path: '/text-to-audio',
|
||||
summary: 'Convert text to audio',
|
||||
tags: ['web'],
|
||||
})
|
||||
.input(z.object({ body: zPostTextToAudioBody }))
|
||||
.output(zPostTextToAudioResponse)
|
||||
|
||||
export const textToAudio = {
|
||||
post: post21,
|
||||
get: get12,
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the access mode for a web application (public or restricted).
|
||||
*/
|
||||
export const get14 = oc
|
||||
export const get13 = oc
|
||||
.route({
|
||||
description: 'Retrieve the access mode for a web application (public or restricted).',
|
||||
inputStructure: 'detailed',
|
||||
@ -1009,13 +924,13 @@ export const get14 = oc
|
||||
.output(zGetWebappAccessModeResponse)
|
||||
|
||||
export const accessMode = {
|
||||
get: get14,
|
||||
get: get13,
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to access a web application.
|
||||
*/
|
||||
export const get15 = oc
|
||||
export const get14 = oc
|
||||
.route({
|
||||
description: 'Check if user has permission to access a web application.',
|
||||
inputStructure: 'detailed',
|
||||
@ -1028,7 +943,7 @@ export const get15 = oc
|
||||
.output(zGetWebappPermissionResponse)
|
||||
|
||||
export const permission = {
|
||||
get: get15,
|
||||
get: get14,
|
||||
}
|
||||
|
||||
export const webapp = {
|
||||
@ -1043,7 +958,7 @@ export const webapp = {
|
||||
*
|
||||
* Returns Server-Sent Events stream.
|
||||
*/
|
||||
export const get16 = oc
|
||||
export const get15 = oc
|
||||
.route({
|
||||
description: 'GET /api/workflow/<task_id>/events\n\nReturns Server-Sent Events stream.',
|
||||
inputStructure: 'detailed',
|
||||
@ -1057,7 +972,7 @@ export const get16 = oc
|
||||
.output(zGetWorkflowByTaskIdEventsResponse)
|
||||
|
||||
export const events = {
|
||||
get: get16,
|
||||
get: get15,
|
||||
}
|
||||
|
||||
export const byTaskId3 = {
|
||||
@ -1068,34 +983,12 @@ export const workflow = {
|
||||
byTaskId: byTaskId3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Run workflow
|
||||
*
|
||||
* Execute a workflow with provided inputs and files.
|
||||
*/
|
||||
export const post22 = oc
|
||||
.route({
|
||||
description: 'Execute a workflow with provided inputs and files.',
|
||||
inputStructure: 'detailed',
|
||||
method: 'POST',
|
||||
operationId: 'postWorkflowsRun',
|
||||
path: '/workflows/run',
|
||||
summary: 'Run workflow',
|
||||
tags: ['web'],
|
||||
})
|
||||
.input(z.object({ body: zPostWorkflowsRunBody }))
|
||||
.output(zPostWorkflowsRunResponse)
|
||||
|
||||
export const run = {
|
||||
post: post22,
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop workflow task
|
||||
*
|
||||
* Stop a running workflow task.
|
||||
*/
|
||||
export const post23 = oc
|
||||
export const post19 = oc
|
||||
.route({
|
||||
description: 'Stop a running workflow task.',
|
||||
inputStructure: 'detailed',
|
||||
@ -1109,7 +1002,7 @@ export const post23 = oc
|
||||
.output(zPostWorkflowsTasksByTaskIdStopResponse)
|
||||
|
||||
export const stop3 = {
|
||||
post: post23,
|
||||
post: post19,
|
||||
}
|
||||
|
||||
export const byTaskId4 = {
|
||||
@ -1121,7 +1014,6 @@ export const tasks = {
|
||||
}
|
||||
|
||||
export const workflows = {
|
||||
run,
|
||||
tasks,
|
||||
}
|
||||
|
||||
@ -1145,7 +1037,6 @@ export const contract = {
|
||||
savedMessages,
|
||||
site,
|
||||
systemFeatures,
|
||||
textToAudio,
|
||||
webapp,
|
||||
workflow,
|
||||
workflows,
|
||||
|
||||
@ -46,50 +46,7 @@ export type AppPermissionQuery = {
|
||||
appId: string
|
||||
}
|
||||
|
||||
export type AppSiteInfoResponse = {
|
||||
app_id: string
|
||||
can_replace_logo: boolean
|
||||
custom_config?: {
|
||||
[key: string]: unknown
|
||||
} | null
|
||||
enable_site: boolean
|
||||
end_user_id?: string | null
|
||||
model_config?: AppSiteModelConfigResponse | null
|
||||
plan?: string | null
|
||||
site: AppSiteResponse
|
||||
}
|
||||
|
||||
export type AppSiteModelConfigResponse = {
|
||||
model: unknown
|
||||
more_like_this: unknown
|
||||
opening_statement?: string | null
|
||||
pre_prompt?: string | null
|
||||
suggested_questions: unknown
|
||||
suggested_questions_after_answer: unknown
|
||||
user_input_form: unknown
|
||||
}
|
||||
|
||||
export type AppSiteResponse = {
|
||||
chat_color_theme?: string | null
|
||||
chat_color_theme_inverted?: boolean | null
|
||||
copyright?: string | null
|
||||
custom_disclaimer?: string | null
|
||||
default_language?: string | null
|
||||
description?: string | null
|
||||
icon?: string | null
|
||||
icon_background?: string | null
|
||||
icon_type?: string | null
|
||||
icon_url?: string | null
|
||||
privacy_policy?: string | null
|
||||
prompt_public?: boolean | null
|
||||
show_workflow_steps?: boolean | null
|
||||
title?: string | null
|
||||
use_icon_as_answer_icon?: boolean | null
|
||||
}
|
||||
|
||||
export type AudioBinaryResponse = Blob | File
|
||||
|
||||
export type AudioTranscriptResponse = {
|
||||
export type AudioToTextResponse = {
|
||||
text: string
|
||||
}
|
||||
|
||||
@ -256,8 +213,6 @@ export type FormInputConfig
|
||||
type: 'file-list'
|
||||
} & FileListInputConfig)
|
||||
|
||||
export type GeneratedAppResponse = JsonValue
|
||||
|
||||
export type HumanInputContent = {
|
||||
form_definition?: HumanInputFormDefinition | null
|
||||
form_submission_data?: HumanInputFormSubmissionData | null
|
||||
@ -287,15 +242,13 @@ export type HumanInputFormDefinition = {
|
||||
|
||||
export type HumanInputFormDefinitionResponse = {
|
||||
expiration_time: number
|
||||
form_content: unknown
|
||||
inputs: unknown
|
||||
form_content: string
|
||||
inputs: Array<FormInputConfig>
|
||||
resolved_default_values: {
|
||||
[key: string]: string
|
||||
}
|
||||
site?: {
|
||||
[key: string]: unknown
|
||||
} | null
|
||||
user_actions: unknown
|
||||
site?: WebAppSiteResponse | null
|
||||
user_actions: Array<UserActionConfig>
|
||||
}
|
||||
|
||||
export type HumanInputFormSubmissionData = {
|
||||
@ -317,7 +270,7 @@ export type HumanInputFormSubmitPayload = {
|
||||
}
|
||||
|
||||
export type HumanInputFormSubmitResponse = {
|
||||
[key: string]: never
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type HumanInputUploadTokenResponse = {
|
||||
@ -329,16 +282,7 @@ export type JsonObject = {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type JsonValue
|
||||
= | string
|
||||
| number
|
||||
| number
|
||||
| boolean
|
||||
| {
|
||||
[key: string]: unknown
|
||||
}
|
||||
| Array<unknown>
|
||||
| null
|
||||
export type JsonValue = unknown
|
||||
|
||||
export type JsonValueType = unknown
|
||||
|
||||
@ -614,6 +558,22 @@ export type WebAppAuthSsoModel = {
|
||||
protocol: string
|
||||
}
|
||||
|
||||
export type WebAppCustomConfigResponse = {
|
||||
remove_webapp_brand: boolean
|
||||
replace_webapp_logo?: string | null
|
||||
}
|
||||
|
||||
export type WebAppSiteResponse = {
|
||||
app_id: string
|
||||
can_replace_logo: boolean
|
||||
custom_config?: WebAppCustomConfigResponse | null
|
||||
enable_site: boolean
|
||||
end_user_id?: string | null
|
||||
model_config?: WebModelConfigResponse | null
|
||||
plan: string
|
||||
site: WebSiteResponse
|
||||
}
|
||||
|
||||
export type WebMessageInfiniteScrollPagination = {
|
||||
data: Array<WebMessageListItem>
|
||||
has_more: boolean
|
||||
@ -640,6 +600,34 @@ export type WebMessageListItem = {
|
||||
status: string
|
||||
}
|
||||
|
||||
export type WebModelConfigResponse = {
|
||||
model?: unknown
|
||||
more_like_this?: unknown
|
||||
opening_statement?: string | null
|
||||
pre_prompt?: string | null
|
||||
suggested_questions?: unknown
|
||||
suggested_questions_after_answer?: unknown
|
||||
user_input_form?: unknown
|
||||
}
|
||||
|
||||
export type WebSiteResponse = {
|
||||
chat_color_theme?: string | null
|
||||
chat_color_theme_inverted: boolean
|
||||
copyright?: string | null
|
||||
custom_disclaimer?: string | null
|
||||
default_language?: string | null
|
||||
description?: string | null
|
||||
icon?: string | null
|
||||
icon_background?: string | null
|
||||
icon_type?: string | null
|
||||
readonly icon_url: string | null
|
||||
privacy_policy?: string | null
|
||||
prompt_public?: boolean | null
|
||||
show_workflow_steps?: boolean | null
|
||||
title: string
|
||||
use_icon_as_answer_icon?: boolean | null
|
||||
}
|
||||
|
||||
export type WorkflowRunPayload = {
|
||||
files?: Array<{
|
||||
transfer_method: 'local_file' | 'remote_url'
|
||||
@ -652,6 +640,49 @@ export type WorkflowRunPayload = {
|
||||
}
|
||||
}
|
||||
|
||||
export type HumanInputFormDefinitionResponseWritable = {
|
||||
expiration_time: number
|
||||
form_content: string
|
||||
inputs: Array<FormInputConfig>
|
||||
resolved_default_values: {
|
||||
[key: string]: string
|
||||
}
|
||||
site?: WebAppSiteResponseWritable | null
|
||||
user_actions: Array<UserActionConfig>
|
||||
}
|
||||
|
||||
export type HumanInputFormSubmitResponseWritable = {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type WebAppSiteResponseWritable = {
|
||||
app_id: string
|
||||
can_replace_logo: boolean
|
||||
custom_config?: WebAppCustomConfigResponse | null
|
||||
enable_site: boolean
|
||||
end_user_id?: string | null
|
||||
model_config?: WebModelConfigResponse | null
|
||||
plan: string
|
||||
site: WebSiteResponseWritable
|
||||
}
|
||||
|
||||
export type WebSiteResponseWritable = {
|
||||
chat_color_theme?: string | null
|
||||
chat_color_theme_inverted: boolean
|
||||
copyright?: string | null
|
||||
custom_disclaimer?: string | null
|
||||
default_language?: string | null
|
||||
description?: string | null
|
||||
icon?: string | null
|
||||
icon_background?: string | null
|
||||
icon_type?: string | null
|
||||
privacy_policy?: string | null
|
||||
prompt_public?: boolean | null
|
||||
show_workflow_steps?: boolean | null
|
||||
title: string
|
||||
use_icon_as_answer_icon?: boolean | null
|
||||
}
|
||||
|
||||
export type PostAudioToTextData = {
|
||||
body?: never
|
||||
path?: never
|
||||
@ -669,32 +700,11 @@ export type PostAudioToTextErrors = {
|
||||
}
|
||||
|
||||
export type PostAudioToTextResponses = {
|
||||
200: AudioTranscriptResponse
|
||||
200: AudioToTextResponse
|
||||
}
|
||||
|
||||
export type PostAudioToTextResponse = PostAudioToTextResponses[keyof PostAudioToTextResponses]
|
||||
|
||||
export type PostChatMessagesData = {
|
||||
body: ChatMessagePayload
|
||||
path?: never
|
||||
query?: never
|
||||
url: '/chat-messages'
|
||||
}
|
||||
|
||||
export type PostChatMessagesErrors = {
|
||||
400: unknown
|
||||
401: unknown
|
||||
403: unknown
|
||||
404: unknown
|
||||
500: unknown
|
||||
}
|
||||
|
||||
export type PostChatMessagesResponses = {
|
||||
200: GeneratedAppResponse
|
||||
}
|
||||
|
||||
export type PostChatMessagesResponse = PostChatMessagesResponses[keyof PostChatMessagesResponses]
|
||||
|
||||
export type PostChatMessagesByTaskIdStopData = {
|
||||
body?: never
|
||||
path: {
|
||||
@ -719,28 +729,6 @@ export type PostChatMessagesByTaskIdStopResponses = {
|
||||
export type PostChatMessagesByTaskIdStopResponse
|
||||
= PostChatMessagesByTaskIdStopResponses[keyof PostChatMessagesByTaskIdStopResponses]
|
||||
|
||||
export type PostCompletionMessagesData = {
|
||||
body: CompletionMessagePayload
|
||||
path?: never
|
||||
query?: never
|
||||
url: '/completion-messages'
|
||||
}
|
||||
|
||||
export type PostCompletionMessagesErrors = {
|
||||
400: unknown
|
||||
401: unknown
|
||||
403: unknown
|
||||
404: unknown
|
||||
500: unknown
|
||||
}
|
||||
|
||||
export type PostCompletionMessagesResponses = {
|
||||
200: GeneratedAppResponse
|
||||
}
|
||||
|
||||
export type PostCompletionMessagesResponse
|
||||
= PostCompletionMessagesResponses[keyof PostCompletionMessagesResponses]
|
||||
|
||||
export type PostCompletionMessagesByTaskIdStopData = {
|
||||
body?: never
|
||||
path: {
|
||||
@ -1016,6 +1004,13 @@ export type GetFormHumanInputByFormTokenData = {
|
||||
url: '/form/human_input/{form_token}'
|
||||
}
|
||||
|
||||
export type GetFormHumanInputByFormTokenErrors = {
|
||||
403: unknown
|
||||
404: unknown
|
||||
412: unknown
|
||||
429: unknown
|
||||
}
|
||||
|
||||
export type GetFormHumanInputByFormTokenResponses = {
|
||||
200: HumanInputFormDefinitionResponse
|
||||
}
|
||||
@ -1032,6 +1027,13 @@ export type PostFormHumanInputByFormTokenData = {
|
||||
url: '/form/human_input/{form_token}'
|
||||
}
|
||||
|
||||
export type PostFormHumanInputByFormTokenErrors = {
|
||||
400: unknown
|
||||
404: unknown
|
||||
412: unknown
|
||||
429: unknown
|
||||
}
|
||||
|
||||
export type PostFormHumanInputByFormTokenResponses = {
|
||||
200: HumanInputFormSubmitResponse
|
||||
}
|
||||
@ -1048,6 +1050,12 @@ export type PostFormHumanInputByFormTokenUploadTokenData = {
|
||||
url: '/form/human_input/{form_token}/upload-token'
|
||||
}
|
||||
|
||||
export type PostFormHumanInputByFormTokenUploadTokenErrors = {
|
||||
404: unknown
|
||||
412: unknown
|
||||
429: unknown
|
||||
}
|
||||
|
||||
export type PostFormHumanInputByFormTokenUploadTokenResponses = {
|
||||
200: HumanInputUploadTokenResponse
|
||||
}
|
||||
@ -1174,32 +1182,6 @@ export type PostMessagesByMessageIdFeedbacksResponses = {
|
||||
export type PostMessagesByMessageIdFeedbacksResponse
|
||||
= PostMessagesByMessageIdFeedbacksResponses[keyof PostMessagesByMessageIdFeedbacksResponses]
|
||||
|
||||
export type GetMessagesByMessageIdMoreLikeThisData = {
|
||||
body?: never
|
||||
path: {
|
||||
message_id: string
|
||||
}
|
||||
query: {
|
||||
response_mode: 'blocking' | 'streaming'
|
||||
}
|
||||
url: '/messages/{message_id}/more-like-this'
|
||||
}
|
||||
|
||||
export type GetMessagesByMessageIdMoreLikeThisErrors = {
|
||||
400: unknown
|
||||
401: unknown
|
||||
403: unknown
|
||||
404: unknown
|
||||
500: unknown
|
||||
}
|
||||
|
||||
export type GetMessagesByMessageIdMoreLikeThisResponses = {
|
||||
200: GeneratedAppResponse
|
||||
}
|
||||
|
||||
export type GetMessagesByMessageIdMoreLikeThisResponse
|
||||
= GetMessagesByMessageIdMoreLikeThisResponses[keyof GetMessagesByMessageIdMoreLikeThisResponses]
|
||||
|
||||
export type GetMessagesByMessageIdSuggestedQuestionsData = {
|
||||
body?: never
|
||||
path: {
|
||||
@ -1416,7 +1398,7 @@ export type GetSiteErrors = {
|
||||
}
|
||||
|
||||
export type GetSiteResponses = {
|
||||
200: AppSiteInfoResponse
|
||||
200: WebAppSiteResponse
|
||||
}
|
||||
|
||||
export type GetSiteResponse = GetSiteResponses[keyof GetSiteResponses]
|
||||
@ -1438,26 +1420,6 @@ export type GetSystemFeaturesResponses = {
|
||||
|
||||
export type GetSystemFeaturesResponse = GetSystemFeaturesResponses[keyof GetSystemFeaturesResponses]
|
||||
|
||||
export type PostTextToAudioData = {
|
||||
body: TextToAudioPayload
|
||||
path?: never
|
||||
query?: never
|
||||
url: '/text-to-audio'
|
||||
}
|
||||
|
||||
export type PostTextToAudioErrors = {
|
||||
400: unknown
|
||||
401: unknown
|
||||
403: unknown
|
||||
500: unknown
|
||||
}
|
||||
|
||||
export type PostTextToAudioResponses = {
|
||||
200: AudioBinaryResponse
|
||||
}
|
||||
|
||||
export type PostTextToAudioResponse = PostTextToAudioResponses[keyof PostTextToAudioResponses]
|
||||
|
||||
export type GetWebappAccessModeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
@ -1518,27 +1480,6 @@ export type GetWorkflowByTaskIdEventsResponses = {
|
||||
export type GetWorkflowByTaskIdEventsResponse
|
||||
= GetWorkflowByTaskIdEventsResponses[keyof GetWorkflowByTaskIdEventsResponses]
|
||||
|
||||
export type PostWorkflowsRunData = {
|
||||
body: WorkflowRunPayload
|
||||
path?: never
|
||||
query?: never
|
||||
url: '/workflows/run'
|
||||
}
|
||||
|
||||
export type PostWorkflowsRunErrors = {
|
||||
400: unknown
|
||||
401: unknown
|
||||
403: unknown
|
||||
404: unknown
|
||||
500: unknown
|
||||
}
|
||||
|
||||
export type PostWorkflowsRunResponses = {
|
||||
200: GeneratedAppResponse
|
||||
}
|
||||
|
||||
export type PostWorkflowsRunResponse = PostWorkflowsRunResponses[keyof PostWorkflowsRunResponses]
|
||||
|
||||
export type PostWorkflowsTasksByTaskIdStopData = {
|
||||
body?: never
|
||||
path: {
|
||||
|
||||
@ -47,62 +47,9 @@ export const zAppPermissionQuery = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* AppSiteModelConfigResponse
|
||||
* AudioToTextResponse
|
||||
*/
|
||||
export const zAppSiteModelConfigResponse = z.object({
|
||||
model: z.unknown(),
|
||||
more_like_this: z.unknown(),
|
||||
opening_statement: z.string().nullish(),
|
||||
pre_prompt: z.string().nullish(),
|
||||
suggested_questions: z.unknown(),
|
||||
suggested_questions_after_answer: z.unknown(),
|
||||
user_input_form: z.unknown(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppSiteResponse
|
||||
*/
|
||||
export const zAppSiteResponse = z.object({
|
||||
chat_color_theme: z.string().nullish(),
|
||||
chat_color_theme_inverted: z.boolean().nullish(),
|
||||
copyright: z.string().nullish(),
|
||||
custom_disclaimer: z.string().nullish(),
|
||||
default_language: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullish(),
|
||||
privacy_policy: z.string().nullish(),
|
||||
prompt_public: z.boolean().nullish(),
|
||||
show_workflow_steps: z.boolean().nullish(),
|
||||
title: z.string().nullish(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AppSiteInfoResponse
|
||||
*/
|
||||
export const zAppSiteInfoResponse = z.object({
|
||||
app_id: z.string(),
|
||||
can_replace_logo: z.boolean(),
|
||||
custom_config: z.record(z.string(), z.unknown()).nullish(),
|
||||
enable_site: z.boolean(),
|
||||
end_user_id: z.string().nullish(),
|
||||
model_config: zAppSiteModelConfigResponse.nullish(),
|
||||
plan: z.string().nullish(),
|
||||
site: zAppSiteResponse,
|
||||
})
|
||||
|
||||
/**
|
||||
* AudioBinaryResponse
|
||||
*/
|
||||
export const zAudioBinaryResponse = z.custom<Blob | File>()
|
||||
|
||||
/**
|
||||
* AudioTranscriptResponse
|
||||
*/
|
||||
export const zAudioTranscriptResponse = z.object({
|
||||
export const zAudioToTextResponse = z.object({
|
||||
text: z.string(),
|
||||
})
|
||||
|
||||
@ -320,22 +267,10 @@ export const zHumanInputFileUploadFormPayload = z.object({
|
||||
url: z.url().min(1).max(2083).nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* HumanInputFormDefinitionResponse
|
||||
*/
|
||||
export const zHumanInputFormDefinitionResponse = z.object({
|
||||
expiration_time: z.int(),
|
||||
form_content: z.unknown(),
|
||||
inputs: z.unknown(),
|
||||
resolved_default_values: z.record(z.string(), z.string()),
|
||||
site: z.record(z.string(), z.unknown()).nullish(),
|
||||
user_actions: z.unknown(),
|
||||
})
|
||||
|
||||
/**
|
||||
* HumanInputFormSubmitResponse
|
||||
*/
|
||||
export const zHumanInputFormSubmitResponse = z.record(z.string(), z.never())
|
||||
export const zHumanInputFormSubmitResponse = z.record(z.string(), z.unknown())
|
||||
|
||||
/**
|
||||
* HumanInputUploadTokenResponse
|
||||
@ -347,16 +282,7 @@ export const zHumanInputUploadTokenResponse = z.object({
|
||||
|
||||
export const zJsonObject = z.record(z.string(), z.unknown())
|
||||
|
||||
export const zJsonValue = z
|
||||
.union([
|
||||
z.string(),
|
||||
z.int(),
|
||||
z.number(),
|
||||
z.boolean(),
|
||||
z.record(z.string(), z.unknown()),
|
||||
z.array(z.unknown()),
|
||||
])
|
||||
.nullable()
|
||||
export const zJsonValue = z.unknown()
|
||||
|
||||
/**
|
||||
* AgentThought
|
||||
@ -375,11 +301,6 @@ export const zAgentThought = z.object({
|
||||
tool_labels: zJsonValue,
|
||||
})
|
||||
|
||||
/**
|
||||
* GeneratedAppResponse
|
||||
*/
|
||||
export const zGeneratedAppResponse = zJsonValue
|
||||
|
||||
export const zJsonValueType = z.unknown()
|
||||
|
||||
export const zJsonValue2 = z.unknown()
|
||||
@ -874,6 +795,14 @@ export const zSystemFeatureModel = z.object({
|
||||
}),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebAppCustomConfigResponse
|
||||
*/
|
||||
export const zWebAppCustomConfigResponse = z.object({
|
||||
remove_webapp_brand: z.boolean(),
|
||||
replace_webapp_logo: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebMessageListItem
|
||||
*/
|
||||
@ -904,6 +833,66 @@ export const zWebMessageInfiniteScrollPagination = z.object({
|
||||
limit: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebModelConfigResponse
|
||||
*/
|
||||
export const zWebModelConfigResponse = z.object({
|
||||
model: z.unknown().optional(),
|
||||
more_like_this: z.unknown().optional(),
|
||||
opening_statement: z.string().nullish(),
|
||||
pre_prompt: z.string().nullish(),
|
||||
suggested_questions: z.unknown().optional(),
|
||||
suggested_questions_after_answer: z.unknown().optional(),
|
||||
user_input_form: z.unknown().optional(),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebSiteResponse
|
||||
*/
|
||||
export const zWebSiteResponse = z.object({
|
||||
chat_color_theme: z.string().nullish(),
|
||||
chat_color_theme_inverted: z.boolean(),
|
||||
copyright: z.string().nullish(),
|
||||
custom_disclaimer: z.string().nullish(),
|
||||
default_language: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
icon_url: z.string().nullable(),
|
||||
privacy_policy: z.string().nullish(),
|
||||
prompt_public: z.boolean().nullish(),
|
||||
show_workflow_steps: z.boolean().nullish(),
|
||||
title: z.string(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebAppSiteResponse
|
||||
*/
|
||||
export const zWebAppSiteResponse = z.object({
|
||||
app_id: z.string(),
|
||||
can_replace_logo: z.boolean(),
|
||||
custom_config: zWebAppCustomConfigResponse.nullish(),
|
||||
enable_site: z.boolean(),
|
||||
end_user_id: z.string().nullish(),
|
||||
model_config: zWebModelConfigResponse.nullish(),
|
||||
plan: z.string(),
|
||||
site: zWebSiteResponse,
|
||||
})
|
||||
|
||||
/**
|
||||
* HumanInputFormDefinitionResponse
|
||||
*/
|
||||
export const zHumanInputFormDefinitionResponse = z.object({
|
||||
expiration_time: z.int(),
|
||||
form_content: z.string(),
|
||||
inputs: z.array(zFormInputConfig),
|
||||
resolved_default_values: z.record(z.string(), z.string()),
|
||||
site: zWebAppSiteResponse.nullish(),
|
||||
user_actions: z.array(zUserActionConfig),
|
||||
})
|
||||
|
||||
/**
|
||||
* WorkflowRunPayload
|
||||
*/
|
||||
@ -922,16 +911,60 @@ export const zWorkflowRunPayload = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
* HumanInputFormSubmitResponse
|
||||
*/
|
||||
export const zPostAudioToTextResponse = zAudioTranscriptResponse
|
||||
export const zHumanInputFormSubmitResponseWritable = z.record(z.string(), z.unknown())
|
||||
|
||||
export const zPostChatMessagesBody = zChatMessagePayload
|
||||
/**
|
||||
* WebSiteResponse
|
||||
*/
|
||||
export const zWebSiteResponseWritable = z.object({
|
||||
chat_color_theme: z.string().nullish(),
|
||||
chat_color_theme_inverted: z.boolean(),
|
||||
copyright: z.string().nullish(),
|
||||
custom_disclaimer: z.string().nullish(),
|
||||
default_language: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
icon: z.string().nullish(),
|
||||
icon_background: z.string().nullish(),
|
||||
icon_type: z.string().nullish(),
|
||||
privacy_policy: z.string().nullish(),
|
||||
prompt_public: z.boolean().nullish(),
|
||||
show_workflow_steps: z.boolean().nullish(),
|
||||
title: z.string(),
|
||||
use_icon_as_answer_icon: z.boolean().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* WebAppSiteResponse
|
||||
*/
|
||||
export const zWebAppSiteResponseWritable = z.object({
|
||||
app_id: z.string(),
|
||||
can_replace_logo: z.boolean(),
|
||||
custom_config: zWebAppCustomConfigResponse.nullish(),
|
||||
enable_site: z.boolean(),
|
||||
end_user_id: z.string().nullish(),
|
||||
model_config: zWebModelConfigResponse.nullish(),
|
||||
plan: z.string(),
|
||||
site: zWebSiteResponseWritable,
|
||||
})
|
||||
|
||||
/**
|
||||
* HumanInputFormDefinitionResponse
|
||||
*/
|
||||
export const zHumanInputFormDefinitionResponseWritable = z.object({
|
||||
expiration_time: z.int(),
|
||||
form_content: z.string(),
|
||||
inputs: z.array(zFormInputConfig),
|
||||
resolved_default_values: z.record(z.string(), z.string()),
|
||||
site: zWebAppSiteResponseWritable.nullish(),
|
||||
user_actions: z.array(zUserActionConfig),
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zPostChatMessagesResponse = zGeneratedAppResponse
|
||||
export const zPostAudioToTextResponse = zAudioToTextResponse
|
||||
|
||||
export const zPostChatMessagesByTaskIdStopPath = z.object({
|
||||
task_id: z.string(),
|
||||
@ -942,13 +975,6 @@ export const zPostChatMessagesByTaskIdStopPath = z.object({
|
||||
*/
|
||||
export const zPostChatMessagesByTaskIdStopResponse = zSimpleResultResponse
|
||||
|
||||
export const zPostCompletionMessagesBody = zCompletionMessagePayload
|
||||
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zPostCompletionMessagesResponse = zGeneratedAppResponse
|
||||
|
||||
export const zPostCompletionMessagesByTaskIdStopPath = z.object({
|
||||
task_id: z.string(),
|
||||
})
|
||||
@ -1061,7 +1087,7 @@ export const zGetFormHumanInputByFormTokenPath = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
* Form retrieved successfully
|
||||
*/
|
||||
export const zGetFormHumanInputByFormTokenResponse = zHumanInputFormDefinitionResponse
|
||||
|
||||
@ -1072,7 +1098,7 @@ export const zPostFormHumanInputByFormTokenPath = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
* Form submitted successfully
|
||||
*/
|
||||
export const zPostFormHumanInputByFormTokenResponse = zHumanInputFormSubmitResponse
|
||||
|
||||
@ -1081,7 +1107,7 @@ export const zPostFormHumanInputByFormTokenUploadTokenPath = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
* Upload token issued successfully
|
||||
*/
|
||||
export const zPostFormHumanInputByFormTokenUploadTokenResponse = zHumanInputUploadTokenResponse
|
||||
|
||||
@ -1139,19 +1165,6 @@ export const zPostMessagesByMessageIdFeedbacksQuery = z.object({
|
||||
*/
|
||||
export const zPostMessagesByMessageIdFeedbacksResponse = zResultResponse
|
||||
|
||||
export const zGetMessagesByMessageIdMoreLikeThisPath = z.object({
|
||||
message_id: z.uuid(),
|
||||
})
|
||||
|
||||
export const zGetMessagesByMessageIdMoreLikeThisQuery = z.object({
|
||||
response_mode: z.enum(['blocking', 'streaming']),
|
||||
})
|
||||
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zGetMessagesByMessageIdMoreLikeThisResponse = zGeneratedAppResponse
|
||||
|
||||
export const zGetMessagesByMessageIdSuggestedQuestionsPath = z.object({
|
||||
message_id: z.uuid(),
|
||||
})
|
||||
@ -1229,20 +1242,13 @@ export const zDeleteSavedMessagesByMessageIdResponse = z.void()
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zGetSiteResponse = zAppSiteInfoResponse
|
||||
export const zGetSiteResponse = zWebAppSiteResponse
|
||||
|
||||
/**
|
||||
* System features retrieved successfully
|
||||
*/
|
||||
export const zGetSystemFeaturesResponse = zSystemFeatureModel
|
||||
|
||||
export const zPostTextToAudioBody = zTextToAudioPayload
|
||||
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zPostTextToAudioResponse = zAudioBinaryResponse
|
||||
|
||||
export const zGetWebappAccessModeQuery = z.object({
|
||||
appCode: z.string().optional(),
|
||||
appId: z.string().optional(),
|
||||
@ -1271,13 +1277,6 @@ export const zGetWorkflowByTaskIdEventsPath = z.object({
|
||||
*/
|
||||
export const zGetWorkflowByTaskIdEventsResponse = zEventStreamResponse
|
||||
|
||||
export const zPostWorkflowsRunBody = zWorkflowRunPayload
|
||||
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zPostWorkflowsRunResponse = zGeneratedAppResponse
|
||||
|
||||
export const zPostWorkflowsTasksByTaskIdStopPath = z.object({
|
||||
task_id: z.string(),
|
||||
})
|
||||
|
||||
@ -10,13 +10,21 @@ type SwaggerSchema = JsonObject & {
|
||||
$ref?: string
|
||||
}
|
||||
|
||||
type OpenApiMediaType = JsonObject & {
|
||||
schema?: SwaggerSchema
|
||||
}
|
||||
|
||||
type OpenApiResponse = JsonObject & {
|
||||
content?: Record<string, OpenApiMediaType>
|
||||
}
|
||||
|
||||
type OpenApiComponents = JsonObject & {
|
||||
schemas?: Record<string, SwaggerSchema>
|
||||
}
|
||||
|
||||
type SwaggerOperation = JsonObject & {
|
||||
operationId?: string
|
||||
responses?: Record<string, unknown>
|
||||
responses?: Record<string, OpenApiResponse>
|
||||
}
|
||||
|
||||
type SwaggerDocument = JsonObject & {
|
||||
@ -52,6 +60,17 @@ const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
||||
const apiOpenApiDir = path.resolve(currentDir, 'openapi')
|
||||
|
||||
const operationMethods = new Set(['delete', 'get', 'patch', 'post', 'put'])
|
||||
const pydanticDecimalStringPattern = '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'
|
||||
const codegenSafeDecimalStringPattern = '^(?![-+.]*$)[+-]?0*\\d*\\.?\\d*$'
|
||||
|
||||
const opaqueJsonContent = (): Record<string, OpenApiMediaType> => ({
|
||||
'application/json': {
|
||||
schema: {
|
||||
additionalProperties: true,
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const apiSpecs: ApiSpec[] = [
|
||||
{ filename: 'console-openapi.json', name: 'console' },
|
||||
@ -182,6 +201,46 @@ const addOperationIds = (document: SwaggerDocument) => {
|
||||
}
|
||||
}
|
||||
|
||||
const isOpaqueContractResponse = (response: OpenApiResponse) => {
|
||||
const content = response.content
|
||||
if (!isObject(content))
|
||||
return false
|
||||
|
||||
return Object.entries(content).some(([mediaType, media]) => {
|
||||
if (!isObject(media))
|
||||
return false
|
||||
|
||||
return (mediaType === 'application/json' || mediaType === 'text/event-stream') && !('schema' in media)
|
||||
})
|
||||
}
|
||||
|
||||
const hasOpaqueContractSuccessResponse = (operation: SwaggerOperation) => {
|
||||
return Object.entries(operation.responses ?? {}).some(([status, response]) => {
|
||||
return /^2\d\d$/.test(status) && isObject(response) && isOpaqueContractResponse(response)
|
||||
})
|
||||
}
|
||||
|
||||
const normalizeOpaqueContractResponses = (document: SwaggerDocument) => {
|
||||
// Some backend endpoints has no schema (e.g. external) and will trap heyapi here
|
||||
// So we forge an opaque schema here
|
||||
for (const pathItem of Object.values(document.paths ?? {})) {
|
||||
for (const [method, operation] of Object.entries(pathItem)) {
|
||||
if (!operationMethods.has(method) || !isObject(operation))
|
||||
continue
|
||||
|
||||
const swaggerOperation = operation as SwaggerOperation
|
||||
if (!hasOpaqueContractSuccessResponse(swaggerOperation))
|
||||
continue
|
||||
|
||||
Object.values(swaggerOperation.responses ?? {})
|
||||
.filter(response => isObject(response) && isOpaqueContractResponse(response))
|
||||
.forEach((response) => {
|
||||
response.content = opaqueJsonContent()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hasSuccessResponse = (operation: SwaggerOperation) => {
|
||||
return Object.entries(operation.responses ?? {}).some(([status, response]) => {
|
||||
if (!/^2\d\d$/.test(status))
|
||||
@ -215,6 +274,7 @@ const filterContractOperations = (document: SwaggerDocument) => {
|
||||
}
|
||||
|
||||
const normalizeApiSwagger = (document: SwaggerDocument) => {
|
||||
normalizeOpaqueContractResponses(document)
|
||||
filterContractOperations(document)
|
||||
addOperationIds(document)
|
||||
|
||||
@ -380,10 +440,20 @@ const createApiConfig = (job: ApiJob): UserConfig => ({
|
||||
'name': 'zod',
|
||||
'~resolvers': {
|
||||
string: (ctx) => {
|
||||
if (ctx.schema.format !== 'binary')
|
||||
return undefined
|
||||
if (ctx.schema.format === 'binary')
|
||||
return $(ctx.symbols.z).attr('custom').call().generic($.type.or($.type('Blob'), $.type('File')))
|
||||
|
||||
return $(ctx.symbols.z).attr('custom').call().generic($.type.or($.type('Blob'), $.type('File')))
|
||||
if (ctx.schema.pattern === pydanticDecimalStringPattern) {
|
||||
// the pydantic generated regex will emit error like
|
||||
// regexp/no-useless-assertions, so patch the regex here
|
||||
return $(ctx.symbols.z)
|
||||
.attr('string')
|
||||
.call()
|
||||
.attr('regex')
|
||||
.call($.regexp(codegenSafeDecimalStringPattern))
|
||||
}
|
||||
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -36,7 +36,7 @@ const HumanInputForm = ({
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const isActionDisabled = isSubmitting || hasInvalidSelectOrFileInput(renderedFormInputs, inputs)
|
||||
const isActionDisabled = isSubmitting || !formToken || hasInvalidSelectOrFileInput(renderedFormInputs, inputs)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -55,7 +55,7 @@ const HumanInputForm = ({
|
||||
key={action.id}
|
||||
disabled={isActionDisabled}
|
||||
variant={getButtonStyle(action.button_style) as ButtonProps['variant']}
|
||||
onClick={() => submit(formToken, action.id, inputs)}
|
||||
onClick={() => formToken && submit(formToken, action.id, inputs)}
|
||||
>
|
||||
{action.title}
|
||||
</Button>
|
||||
|
||||
@ -88,7 +88,7 @@ export const triggerSubscriptionUpdateContract = base
|
||||
credentials?: Record<string, unknown>
|
||||
}
|
||||
}>())
|
||||
.output(type<{ result: string, id: string }>())
|
||||
.output(type<{ result: string }>())
|
||||
|
||||
export const triggerSubscriptionBuilderLogsContract = base
|
||||
.route({ path: '/workspaces/current/trigger-provider/{provider}/subscriptions/builder/logs/{subscriptionBuilderId}', method: 'GET' })
|
||||
|
||||
@ -217,14 +217,8 @@ const toFeedback = (feedback: NonNullable<MessageDetailResponse['feedbacks']>[nu
|
||||
}
|
||||
}
|
||||
|
||||
type AgentDebugMessageWithLegacyAnswer = MessageDetailResponse & {
|
||||
answer?: string | null
|
||||
}
|
||||
|
||||
const getAgentDebugMessageAnswer = (message: MessageDetailResponse) => {
|
||||
const legacyAnswer = (message as AgentDebugMessageWithLegacyAnswer).answer
|
||||
|
||||
return message.re_sign_file_url_answer ?? legacyAnswer ?? ''
|
||||
return message.answer ?? ''
|
||||
}
|
||||
|
||||
function getFormattedAgentDebugChatTree(messages: MessageDetailResponse[]): ChatItemInTree[] {
|
||||
|
||||
@ -342,10 +342,10 @@ export type HumanInputFormData = {
|
||||
form_content: string
|
||||
inputs: FormInputItem[]
|
||||
actions: UserAction[]
|
||||
form_token: string
|
||||
form_token: string | null
|
||||
resolved_default_values: Record<string, HumanInputResolvedValue>
|
||||
display_in_ui: boolean
|
||||
expiration_time: number
|
||||
expiration_time: number | null
|
||||
}
|
||||
|
||||
export type HumanInputRequiredResponse = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user