mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 02:28:30 +08:00
refactor(api): migrate console app chat endpoints to BaseModel (#37952)
Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
61650d34ce
commit
5d0576de0a
@ -1,7 +1,7 @@
|
||||
from typing import Any, Literal
|
||||
from uuid import UUID
|
||||
|
||||
from flask import abort, make_response, request
|
||||
from flask import abort, request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, TypeAdapter, field_validator
|
||||
from sqlalchemy import select
|
||||
@ -28,10 +28,12 @@ from fields.annotation_fields import (
|
||||
AnnotationExportList,
|
||||
AnnotationHitHistory,
|
||||
AnnotationHitHistoryList,
|
||||
AnnotationJobStatusDetailResponse,
|
||||
AnnotationJobStatusResponse,
|
||||
AnnotationList,
|
||||
)
|
||||
from fields.base import ResponseModel
|
||||
from libs.helper import uuid_value
|
||||
from libs.helper import dump_response, uuid_value
|
||||
from libs.login import current_account_with_tenant, login_required
|
||||
from models.model import App
|
||||
from services.annotation_service import (
|
||||
@ -113,23 +115,23 @@ class AnnotationFilePayload(BaseModel):
|
||||
return uuid_value(value)
|
||||
|
||||
|
||||
class AnnotationJobStatusResponse(ResponseModel):
|
||||
job_id: str | None = None
|
||||
job_status: str | None = None
|
||||
error_msg: str | None = None
|
||||
record_count: int | None = None
|
||||
|
||||
|
||||
class AnnotationEmbeddingModelResponse(ResponseModel):
|
||||
class AnnotationSettingEmbeddingModelResponse(ResponseModel):
|
||||
embedding_provider_name: str | None = None
|
||||
embedding_model_name: str | None = None
|
||||
|
||||
|
||||
class AnnotationSettingResponse(ResponseModel):
|
||||
id: str | None = None
|
||||
enabled: bool
|
||||
id: str | None = None
|
||||
score_threshold: float | None = None
|
||||
embedding_model: AnnotationEmbeddingModelResponse | None = None
|
||||
embedding_model: AnnotationSettingEmbeddingModelResponse | None = None
|
||||
|
||||
|
||||
class AnnotationBatchImportResponse(ResponseModel):
|
||||
job_id: str | None = None
|
||||
job_status: str | None = None
|
||||
record_count: int | None = None
|
||||
error_msg: str | None = None
|
||||
|
||||
|
||||
register_schema_models(
|
||||
@ -156,7 +158,10 @@ register_response_schema_models(
|
||||
AnnotationHitHistory,
|
||||
AnnotationHitHistoryList,
|
||||
AnnotationJobStatusResponse,
|
||||
AnnotationJobStatusDetailResponse,
|
||||
AnnotationSettingEmbeddingModelResponse,
|
||||
AnnotationSettingResponse,
|
||||
AnnotationBatchImportResponse,
|
||||
)
|
||||
|
||||
|
||||
@ -186,7 +191,7 @@ class AnnotationReplyActionApi(Resource):
|
||||
result = AppAnnotationService.enable_app_annotation(enable_args, str(app_id))
|
||||
case "disable":
|
||||
result = AppAnnotationService.disable_app_annotation(str(app_id))
|
||||
return result, 200
|
||||
return dump_response(AnnotationJobStatusResponse, result), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotation-setting")
|
||||
@ -207,7 +212,7 @@ class AppAnnotationSettingDetailApi(Resource):
|
||||
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_VIEW_LAYOUT)
|
||||
def get(self, app_id: UUID):
|
||||
result = AppAnnotationService.get_app_annotation_setting_by_app_id(str(app_id))
|
||||
return result, 200
|
||||
return dump_response(AnnotationSettingResponse, result), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotation-settings/<uuid:annotation_setting_id>")
|
||||
@ -232,7 +237,7 @@ class AppAnnotationSettingUpdateApi(Resource):
|
||||
result = AppAnnotationService.update_app_annotation_setting(
|
||||
str(app_id), annotation_setting_id_str, setting_args
|
||||
)
|
||||
return result, 200
|
||||
return dump_response(AnnotationSettingResponse, result), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotation-reply/<string:action>/status/<uuid:job_id>")
|
||||
@ -241,9 +246,7 @@ class AnnotationReplyActionStatusApi(Resource):
|
||||
@console_ns.doc(description="Get status of annotation reply action job")
|
||||
@console_ns.doc(params={"app_id": "Application ID", "job_id": "Job ID", "action": "Action type"})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Job status retrieved successfully",
|
||||
console_ns.models[AnnotationJobStatusResponse.__name__],
|
||||
200, "Job status retrieved successfully", console_ns.models[AnnotationJobStatusDetailResponse.__name__]
|
||||
)
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@setup_required
|
||||
@ -265,7 +268,9 @@ class AnnotationReplyActionStatusApi(Resource):
|
||||
app_annotation_error_key = f"{action}_app_annotation_error_{job_id_str}"
|
||||
error_msg = redis_client.get(app_annotation_error_key).decode()
|
||||
|
||||
return {"job_id": job_id_str, "job_status": job_status, "error_msg": error_msg}, 200
|
||||
return AnnotationJobStatusDetailResponse(
|
||||
job_id=job_id_str, job_status=job_status, error_msg=error_msg
|
||||
).model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotations")
|
||||
@ -289,14 +294,9 @@ class AnnotationApi(Resource):
|
||||
|
||||
annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(str(app_id), page, limit, keyword)
|
||||
annotation_models = TypeAdapter(list[Annotation]).validate_python(annotation_list, from_attributes=True)
|
||||
response = AnnotationList(
|
||||
data=annotation_models,
|
||||
has_more=len(annotation_list) == limit,
|
||||
limit=limit,
|
||||
total=total,
|
||||
page=page,
|
||||
)
|
||||
return response.model_dump(mode="json"), 200
|
||||
return AnnotationList(
|
||||
data=annotation_models, has_more=len(annotation_list) == limit, limit=limit, total=total, page=page
|
||||
).model_dump(mode="json"), 200
|
||||
|
||||
@console_ns.doc("create_annotation")
|
||||
@console_ns.doc(description="Create a new annotation for an app")
|
||||
@ -322,7 +322,7 @@ class AnnotationApi(Resource):
|
||||
if args.question is not None:
|
||||
upsert_args["question"] = args.question
|
||||
annotation = AppAnnotationService.up_insert_app_annotation_from_message(upsert_args, str(app_id))
|
||||
return Annotation.model_validate(annotation, from_attributes=True).model_dump(mode="json")
|
||||
return dump_response(Annotation, annotation), 201
|
||||
|
||||
@setup_required
|
||||
@login_required
|
||||
@ -372,14 +372,14 @@ class AnnotationExportApi(Resource):
|
||||
def get(self, app_id: UUID):
|
||||
annotation_list = AppAnnotationService.export_annotation_list_by_app_id(str(app_id))
|
||||
annotation_models = TypeAdapter(list[Annotation]).validate_python(annotation_list, from_attributes=True)
|
||||
response_data = AnnotationExportList(data=annotation_models).model_dump(mode="json")
|
||||
|
||||
# Create response with secure headers for CSV export
|
||||
response = make_response(response_data, 200)
|
||||
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
||||
response.headers["X-Content-Type-Options"] = "nosniff"
|
||||
|
||||
return response
|
||||
return (
|
||||
AnnotationExportList(data=annotation_models).model_dump(mode="json"),
|
||||
200,
|
||||
{
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotations/<uuid:annotation_id>")
|
||||
@ -428,9 +428,7 @@ class AnnotationBatchImportApi(Resource):
|
||||
@console_ns.doc(description="Batch import annotations from CSV file with rate limiting and security checks")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Batch import started successfully",
|
||||
console_ns.models[AnnotationJobStatusResponse.__name__],
|
||||
200, "Batch import started successfully", console_ns.models[AnnotationBatchImportResponse.__name__]
|
||||
)
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@console_ns.response(400, "No file uploaded or too many files")
|
||||
@ -477,7 +475,10 @@ class AnnotationBatchImportApi(Resource):
|
||||
if file_size == 0:
|
||||
raise ValueError("The uploaded file is empty")
|
||||
|
||||
return AppAnnotationService.batch_import_app_annotations(str(app_id), file)
|
||||
return dump_response(
|
||||
AnnotationBatchImportResponse,
|
||||
AppAnnotationService.batch_import_app_annotations(str(app_id), file),
|
||||
)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotations/batch-import-status/<uuid:job_id>")
|
||||
@ -486,9 +487,7 @@ class AnnotationBatchImportStatusApi(Resource):
|
||||
@console_ns.doc(description="Get status of batch import job")
|
||||
@console_ns.doc(params={"app_id": "Application ID", "job_id": "Job ID"})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Job status retrieved successfully",
|
||||
console_ns.models[AnnotationJobStatusResponse.__name__],
|
||||
200, "Job status retrieved successfully", console_ns.models[AnnotationJobStatusDetailResponse.__name__]
|
||||
)
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@setup_required
|
||||
@ -508,7 +507,9 @@ class AnnotationBatchImportStatusApi(Resource):
|
||||
indexing_error_msg_key = f"app_annotation_batch_import_error_msg_{str(job_id)}"
|
||||
error_msg = redis_client.get(indexing_error_msg_key).decode()
|
||||
|
||||
return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200
|
||||
return AnnotationJobStatusDetailResponse(
|
||||
job_id=str(job_id), job_status=job_status, error_msg=error_msg
|
||||
).model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/annotations/<uuid:annotation_id>/hit-histories")
|
||||
@ -541,11 +542,6 @@ class AnnotationHitHistoryListApi(Resource):
|
||||
history_models = TypeAdapter(list[AnnotationHitHistory]).validate_python(
|
||||
annotation_hit_history_list, from_attributes=True
|
||||
)
|
||||
response = AnnotationHitHistoryList(
|
||||
data=history_models,
|
||||
has_more=len(annotation_hit_history_list) == limit,
|
||||
limit=limit,
|
||||
total=total,
|
||||
page=page,
|
||||
)
|
||||
return response.model_dump(mode="json")
|
||||
return AnnotationHitHistoryList(
|
||||
data=history_models, has_more=len(annotation_hit_history_list) == limit, limit=limit, total=total, page=page
|
||||
).model_dump(mode="json")
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
@ -7,7 +6,6 @@ from pydantic import BaseModel, Field, RootModel
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import services
|
||||
from controllers.common.fields import AudioBinaryResponse
|
||||
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.error import (
|
||||
@ -31,7 +29,9 @@ from controllers.console.wraps import (
|
||||
)
|
||||
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 dump_response
|
||||
from libs.login import current_user, login_required
|
||||
from models import App, AppMode
|
||||
from services.app_ref_service import AppRefService
|
||||
@ -57,16 +57,27 @@ class TextToSpeechVoiceQuery(BaseModel):
|
||||
language: str = Field(..., description="Language code")
|
||||
|
||||
|
||||
class AudioTranscriptResponse(BaseModel):
|
||||
class AudioTranscriptResponse(ResponseModel):
|
||||
text: str = Field(description="Transcribed text from audio")
|
||||
|
||||
|
||||
class TextToSpeechVoiceListResponse(RootModel[list[dict[str, Any]]]):
|
||||
root: list[dict[str, Any]]
|
||||
class TextToSpeechVoiceResponse(ResponseModel):
|
||||
# see api/core/plugin/impl/model.py
|
||||
name: str = Field(description="Voice display name")
|
||||
value: str = Field(description="Voice identifier")
|
||||
|
||||
|
||||
register_schema_models(console_ns, AudioTranscriptResponse, TextToSpeechPayload, TextToSpeechVoiceQuery)
|
||||
register_response_schema_models(console_ns, AudioBinaryResponse, TextToSpeechVoiceListResponse)
|
||||
class TextToSpeechVoiceListResponse(RootModel[list[TextToSpeechVoiceResponse]]):
|
||||
root: list[TextToSpeechVoiceResponse] = Field(description="Available voices")
|
||||
|
||||
|
||||
register_schema_models(console_ns, TextToSpeechPayload, TextToSpeechVoiceQuery)
|
||||
register_response_schema_models(
|
||||
console_ns,
|
||||
AudioTranscriptResponse,
|
||||
TextToSpeechVoiceResponse,
|
||||
TextToSpeechVoiceListResponse,
|
||||
)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/audio-to-text")
|
||||
@ -95,7 +106,7 @@ class ChatMessageAudioApi(Resource):
|
||||
end_user=None,
|
||||
)
|
||||
|
||||
return response
|
||||
return dump_response(AudioTranscriptResponse, response)
|
||||
except services.errors.app_model_config.AppModelConfigBrokenError:
|
||||
logger.exception("App model config broken.")
|
||||
raise AppUnavailableError()
|
||||
@ -128,11 +139,8 @@ class ChatMessageTextApi(Resource):
|
||||
@console_ns.doc(description="Convert text to speech for chat messages")
|
||||
@console_ns.doc(params={"app_id": "App ID"})
|
||||
@console_ns.expect(console_ns.models[TextToSpeechPayload.__name__])
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Text to speech conversion successful",
|
||||
console_ns.models[AudioBinaryResponse.__name__],
|
||||
)
|
||||
# TTS returns provider audio bytes, so the success response is intentionally schema-less.
|
||||
@console_ns.response(200, "Text to speech conversion successful")
|
||||
@console_ns.response(400, "Bad request - Invalid parameters")
|
||||
@setup_required
|
||||
@login_required
|
||||
@ -150,7 +158,8 @@ class ChatMessageTextApi(Resource):
|
||||
account_id=current_user.id,
|
||||
)
|
||||
|
||||
response = AudioService.transcript_tts(
|
||||
# response-contract:ignore
|
||||
return AudioService.transcript_tts(
|
||||
app_model=app_model,
|
||||
session=db.session,
|
||||
text=payload.text,
|
||||
@ -158,7 +167,6 @@ class ChatMessageTextApi(Resource):
|
||||
message_ref=message_ref,
|
||||
is_draft=True,
|
||||
)
|
||||
return response
|
||||
except services.errors.app_model_config.AppModelConfigBrokenError:
|
||||
logger.exception("App model config broken.")
|
||||
raise AppUnavailableError()
|
||||
@ -189,8 +197,7 @@ class ChatMessageTextApi(Resource):
|
||||
class TextModesApi(Resource):
|
||||
@console_ns.doc("get_text_to_speech_voices")
|
||||
@console_ns.doc(description="Get available TTS voices for a specific language")
|
||||
@console_ns.doc(params={"app_id": "App ID"})
|
||||
@console_ns.doc(params=query_params_from_model(TextToSpeechVoiceQuery))
|
||||
@console_ns.doc(params={"app_id": "App ID", **query_params_from_model(TextToSpeechVoiceQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"TTS voices retrieved successfully",
|
||||
@ -211,7 +218,7 @@ class TextModesApi(Resource):
|
||||
language=args.language,
|
||||
)
|
||||
|
||||
return response
|
||||
return dump_response(TextToSpeechVoiceListResponse, response)
|
||||
except services.errors.audio.ProviderNotSupportTextToSpeechLanageServiceError:
|
||||
raise AppUnavailableError("Text to audio voices language parameter loss.")
|
||||
except NoAudioUploadedServiceError:
|
||||
|
||||
@ -10,7 +10,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.console import console_ns
|
||||
from controllers.console.agent.app_helpers import resolve_agent_runtime_app_model
|
||||
@ -136,7 +136,7 @@ After config persistence completes, respond FINISHED."""
|
||||
|
||||
|
||||
register_schema_models(console_ns, CompletionMessagePayload, ChatMessagePayload)
|
||||
register_response_schema_models(console_ns, GeneratedAppResponse, SimpleResultResponse)
|
||||
register_response_schema_models(console_ns, SimpleResultResponse)
|
||||
|
||||
|
||||
# define completion message api for user
|
||||
@ -146,7 +146,7 @@ class CompletionMessageApi(Resource):
|
||||
@console_ns.doc(description="Generate completion message for debugging")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.expect(console_ns.models[CompletionMessagePayload.__name__])
|
||||
@console_ns.response(200, "Completion generated successfully", console_ns.models[GeneratedAppResponse.__name__])
|
||||
@console_ns.response(200, "Completion generated successfully")
|
||||
@console_ns.response(400, "Invalid request parameters")
|
||||
@console_ns.response(404, "App not found")
|
||||
@setup_required
|
||||
@ -167,6 +167,7 @@ class CompletionMessageApi(Resource):
|
||||
app_model=app_model, user=current_user, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=streaming
|
||||
)
|
||||
|
||||
# response-contract:ignore compact_generate_response
|
||||
return helper.compact_generate_response(response)
|
||||
except services.errors.conversation.ConversationNotExistsError:
|
||||
raise NotFound("Conversation Not Exists.")
|
||||
@ -210,7 +211,7 @@ class CompletionMessageStopApi(Resource):
|
||||
app_mode=AppMode.value_of(app_model.mode),
|
||||
)
|
||||
|
||||
return {"result": "success"}, 200
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/chat-messages")
|
||||
@ -219,7 +220,7 @@ class ChatMessageApi(Resource):
|
||||
@console_ns.doc(description="Generate chat message for debugging")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.expect(console_ns.models[ChatMessagePayload.__name__])
|
||||
@console_ns.response(200, "Chat message generated successfully", console_ns.models[GeneratedAppResponse.__name__])
|
||||
@console_ns.response(200, "Chat message generated successfully")
|
||||
@console_ns.response(400, "Invalid request parameters")
|
||||
@console_ns.response(404, "App or conversation not found")
|
||||
@setup_required
|
||||
@ -240,7 +241,7 @@ class AgentChatMessageApi(Resource):
|
||||
@console_ns.doc(description="Generate an Agent App chat message for debugging")
|
||||
@console_ns.doc(params={"agent_id": "Agent ID"})
|
||||
@console_ns.expect(console_ns.models[ChatMessagePayload.__name__])
|
||||
@console_ns.response(200, "Chat message generated successfully", console_ns.models[GeneratedAppResponse.__name__])
|
||||
@console_ns.response(200, "Chat message generated successfully")
|
||||
@console_ns.response(400, "Invalid request parameters")
|
||||
@console_ns.response(404, "Agent or conversation not found")
|
||||
@setup_required
|
||||
@ -520,4 +521,4 @@ def _stop_chat_message(*, current_user_id: str, app_model: App, task_id: str):
|
||||
app_mode=AppMode.value_of(app_model.mode),
|
||||
)
|
||||
|
||||
return {"result": "success"}, 200
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json"), 200
|
||||
|
||||
@ -9,7 +9,7 @@ from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import selectinload
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from controllers.common.schema import query_params_from_model, register_schema_models
|
||||
from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.wraps import (
|
||||
@ -39,6 +39,7 @@ from fields.conversation_fields import (
|
||||
ConversationWithSummaryPagination as ConversationWithSummaryPaginationResponse,
|
||||
)
|
||||
from libs.datetime_utils import naive_utc_now, parse_time_range
|
||||
from libs.helper import dump_response
|
||||
from libs.login import login_required
|
||||
from models import Conversation, EndUser, Message, MessageAnnotation
|
||||
from models.account import Account
|
||||
@ -79,13 +80,14 @@ register_schema_models(
|
||||
console_ns,
|
||||
CompletionConversationQuery,
|
||||
ChatConversationQuery,
|
||||
)
|
||||
register_response_schema_models(
|
||||
console_ns,
|
||||
ConversationResponse,
|
||||
ConversationPaginationResponse,
|
||||
ConversationMessageDetailResponse,
|
||||
ConversationWithSummaryPaginationResponse,
|
||||
ConversationDetailResponse,
|
||||
CompletionConversationQuery,
|
||||
ChatConversationQuery,
|
||||
)
|
||||
|
||||
|
||||
@ -93,8 +95,7 @@ register_schema_models(
|
||||
class CompletionConversationApi(Resource):
|
||||
@console_ns.doc("list_completion_conversations")
|
||||
@console_ns.doc(description="Get completion conversations with pagination and filtering")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(CompletionConversationQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(CompletionConversationQuery)})
|
||||
@console_ns.response(200, "Success", console_ns.models[ConversationPaginationResponse.__name__])
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@setup_required
|
||||
@ -157,9 +158,7 @@ class CompletionConversationApi(Resource):
|
||||
|
||||
conversations = db.paginate(query, page=args.page, per_page=args.limit, error_out=False)
|
||||
|
||||
return ConversationPaginationResponse.model_validate(conversations, from_attributes=True).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
return dump_response(ConversationPaginationResponse, conversations)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/completion-conversations/<uuid:conversation_id>")
|
||||
@ -179,9 +178,9 @@ class CompletionConversationDetailApi(Resource):
|
||||
@get_app_model(mode=AppMode.COMPLETION)
|
||||
def get(self, current_user: Account, app_model: App, conversation_id: UUID):
|
||||
conversation_id_str = str(conversation_id)
|
||||
return ConversationMessageDetailResponse.model_validate(
|
||||
_get_conversation(current_user, app_model, conversation_id_str), from_attributes=True
|
||||
).model_dump(mode="json")
|
||||
return dump_response(
|
||||
ConversationMessageDetailResponse, _get_conversation(current_user, app_model, conversation_id_str)
|
||||
)
|
||||
|
||||
@console_ns.doc("delete_completion_conversation")
|
||||
@console_ns.doc(description="Delete a completion conversation")
|
||||
@ -211,8 +210,7 @@ class CompletionConversationDetailApi(Resource):
|
||||
class ChatConversationApi(Resource):
|
||||
@console_ns.doc("list_chat_conversations")
|
||||
@console_ns.doc(description="Get chat conversations with pagination, filtering and summary")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(ChatConversationQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(ChatConversationQuery)})
|
||||
@console_ns.response(200, "Success", console_ns.models[ConversationWithSummaryPaginationResponse.__name__])
|
||||
@console_ns.response(403, "Insufficient permissions")
|
||||
@setup_required
|
||||
@ -314,9 +312,7 @@ class ChatConversationApi(Resource):
|
||||
|
||||
conversations = db.paginate(query, page=args.page, per_page=args.limit, error_out=False)
|
||||
|
||||
return ConversationWithSummaryPaginationResponse.model_validate(conversations, from_attributes=True).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
return dump_response(ConversationWithSummaryPaginationResponse, conversations)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/chat-conversations/<uuid:conversation_id>")
|
||||
@ -336,9 +332,9 @@ class ChatConversationDetailApi(Resource):
|
||||
@get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT])
|
||||
def get(self, current_user: Account, app_model: App, conversation_id: UUID):
|
||||
conversation_id_str = str(conversation_id)
|
||||
return ConversationDetailResponse.model_validate(
|
||||
_get_conversation(current_user, app_model, conversation_id_str), from_attributes=True
|
||||
).model_dump(mode="json")
|
||||
return dump_response(
|
||||
ConversationDetailResponse, _get_conversation(current_user, app_model, conversation_id_str)
|
||||
)
|
||||
|
||||
@console_ns.doc("delete_chat_conversation")
|
||||
@console_ns.doc(description="Delete a chat conversation")
|
||||
|
||||
@ -22,7 +22,7 @@ from controllers.console.wraps import (
|
||||
from extensions.ext_database import db
|
||||
from fields._value_type_serializer import serialize_value_type
|
||||
from fields.base import ResponseModel
|
||||
from libs.helper import to_timestamp
|
||||
from libs.helper import dump_response, to_timestamp
|
||||
from libs.login import login_required
|
||||
from models import ConversationVariable
|
||||
from models.model import App, AppMode
|
||||
@ -119,7 +119,8 @@ class ConversationVariablesApi(Resource):
|
||||
with sessionmaker(db.engine, expire_on_commit=False).begin() as session:
|
||||
rows = session.scalars(stmt).all()
|
||||
|
||||
response = PaginatedConversationVariableResponse.model_validate(
|
||||
return dump_response(
|
||||
PaginatedConversationVariableResponse,
|
||||
{
|
||||
"page": page,
|
||||
"limit": page_size,
|
||||
@ -135,6 +136,5 @@ class ConversationVariablesApi(Resource):
|
||||
)
|
||||
for row in rows
|
||||
],
|
||||
}
|
||||
},
|
||||
)
|
||||
return response.model_dump(mode="json")
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
from uuid import UUID
|
||||
|
||||
@ -38,16 +37,10 @@ from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotIni
|
||||
from extensions.ext_database import db
|
||||
from fields.base import ResponseModel
|
||||
from fields.conversation_fields import (
|
||||
AgentThought,
|
||||
ConversationAnnotation,
|
||||
ConversationAnnotationHitHistory,
|
||||
Feedback,
|
||||
JSONValue,
|
||||
MessageFile,
|
||||
format_files_contained,
|
||||
MessageDetail as BaseMessageDetailResponse,
|
||||
)
|
||||
from graphon.model_runtime.errors.invoke import InvokeError
|
||||
from libs.helper import to_timestamp, uuid_value
|
||||
from libs.helper import dump_response, uuid_value
|
||||
from libs.infinite_scroll_pagination import InfiniteScrollPagination
|
||||
from libs.login import login_required
|
||||
from models.account import Account
|
||||
@ -111,49 +104,16 @@ class FeedbackExportQuery(BaseModel):
|
||||
raise ValueError("has_comment must be a boolean value")
|
||||
|
||||
|
||||
class AnnotationCountResponse(BaseModel):
|
||||
class AnnotationCountResponse(ResponseModel):
|
||||
count: int = Field(description="Number of annotations")
|
||||
|
||||
|
||||
class SuggestedQuestionsResponse(BaseModel):
|
||||
class SuggestedQuestionsResponse(ResponseModel):
|
||||
data: list[str] = Field(description="Suggested question")
|
||||
|
||||
|
||||
class MessageDetailResponse(ResponseModel):
|
||||
id: str
|
||||
conversation_id: str
|
||||
inputs: dict[str, JSONValue]
|
||||
query: str
|
||||
message: JSONValue | None = None
|
||||
message_tokens: int | None = None
|
||||
answer: str = Field(validation_alias="re_sign_file_url_answer")
|
||||
answer_tokens: int | None = None
|
||||
provider_response_latency: float | None = None
|
||||
from_source: str
|
||||
from_end_user_id: str | None = None
|
||||
from_account_id: str | None = None
|
||||
feedbacks: list[Feedback] = Field(default_factory=list)
|
||||
workflow_run_id: str | None = None
|
||||
annotation: ConversationAnnotation | None = None
|
||||
annotation_hit_history: ConversationAnnotationHitHistory | None = None
|
||||
created_at: int | None = None
|
||||
agent_thoughts: list[AgentThought] = Field(default_factory=list)
|
||||
message_files: list[MessageFile] = Field(default_factory=list)
|
||||
class MessageDetailResponse(BaseMessageDetailResponse):
|
||||
extra_contents: list[ExecutionExtraContentDomainModel] = Field(default_factory=list)
|
||||
metadata: JSONValue | None = Field(default=None, validation_alias="message_metadata_dict")
|
||||
status: str
|
||||
error: str | None = None
|
||||
parent_message_id: str | None = None
|
||||
|
||||
@field_validator("inputs", mode="before")
|
||||
@classmethod
|
||||
def _normalize_inputs(cls, value: JSONValue) -> JSONValue:
|
||||
return format_files_contained(value)
|
||||
|
||||
@field_validator("created_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_created_at(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class MessageInfiniteScrollPaginationResponse(ResponseModel):
|
||||
@ -183,8 +143,7 @@ register_response_schema_models(
|
||||
class ChatMessageListApi(Resource):
|
||||
@console_ns.doc("list_chat_messages")
|
||||
@console_ns.doc(description="Get chat messages for a conversation with pagination")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(ChatMessagesQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(ChatMessagesQuery)})
|
||||
@console_ns.response(200, "Success", console_ns.models[MessageInfiniteScrollPaginationResponse.__name__])
|
||||
@console_ns.response(404, "Conversation not found")
|
||||
@login_required
|
||||
@ -274,7 +233,7 @@ class MessageAnnotationCountApi(Resource):
|
||||
select(func.count(MessageAnnotation.id)).where(MessageAnnotation.app_id == app_model.id)
|
||||
)
|
||||
|
||||
return {"count": count}
|
||||
return AnnotationCountResponse(count=count or 0).model_dump(mode="json")
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/chat-messages/<uuid:message_id>/suggested-questions")
|
||||
@ -323,13 +282,12 @@ class AgentMessageSuggestedQuestionApi(Resource):
|
||||
class MessageFeedbackExportApi(Resource):
|
||||
@console_ns.doc("export_feedbacks")
|
||||
@console_ns.doc(description="Export user feedback data for Google Sheets")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(FeedbackExportQuery))
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Feedback data exported successfully",
|
||||
console_ns.models[TextFileResponse.__name__],
|
||||
)
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(FeedbackExportQuery)})
|
||||
@console_ns.response(400, "Invalid parameters")
|
||||
@console_ns.response(500, "Internal server error")
|
||||
@setup_required
|
||||
@ -354,7 +312,6 @@ class MessageFeedbackExportApi(Resource):
|
||||
end_date=args.end_date,
|
||||
format_type=args.format,
|
||||
)
|
||||
|
||||
return export_data
|
||||
|
||||
except ValueError as e:
|
||||
@ -465,16 +422,16 @@ def _list_chat_messages(*, app_model: App, current_user: Account | None = None):
|
||||
history_messages = list(reversed(history_messages))
|
||||
attach_message_extra_contents(history_messages)
|
||||
|
||||
return MessageInfiniteScrollPaginationResponse.model_validate(
|
||||
return dump_response(
|
||||
MessageInfiniteScrollPaginationResponse,
|
||||
InfiniteScrollPagination(data=history_messages, limit=args.limit, has_more=has_more),
|
||||
from_attributes=True,
|
||||
).model_dump(mode="json")
|
||||
)
|
||||
|
||||
|
||||
def _update_message_feedback(*, current_user: Account, app_model: App):
|
||||
args = MessageFeedbackPayload.model_validate(console_ns.payload)
|
||||
|
||||
message_id = str(args.message_id)
|
||||
message_id = args.message_id
|
||||
|
||||
message = db.session.scalar(
|
||||
select(Message).where(Message.id == message_id, Message.app_id == app_model.id).limit(1)
|
||||
@ -509,7 +466,7 @@ def _update_message_feedback(*, current_user: Account, app_model: App):
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return {"result": "success"}
|
||||
return SimpleResultResponse(result="success").model_dump(mode="json")
|
||||
|
||||
|
||||
def _get_message_suggested_questions(*, current_user: Account, app_model: App, message_id: UUID):
|
||||
@ -537,7 +494,7 @@ def _get_message_suggested_questions(*, current_user: Account, app_model: App, m
|
||||
logger.exception("internal server error.")
|
||||
raise InternalServerError()
|
||||
|
||||
return {"data": questions}
|
||||
return dump_response(SuggestedQuestionsResponse, {"data": questions})
|
||||
|
||||
|
||||
def _get_message_detail(*, app_model: App, message_id: UUID):
|
||||
@ -551,4 +508,4 @@ def _get_message_detail(*, app_model: App, message_id: UUID):
|
||||
raise NotFound("Message Not Exists.")
|
||||
|
||||
attach_message_extra_contents([message])
|
||||
return MessageDetailResponse.model_validate(message, from_attributes=True).model_dump(mode="json")
|
||||
return dump_response(MessageDetailResponse, message)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from decimal import Decimal
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask import abort, jsonify, request
|
||||
from flask import abort, request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
@ -20,7 +20,7 @@ from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from extensions.ext_database import db
|
||||
from fields.base import ResponseModel
|
||||
from libs.datetime_utils import parse_time_range
|
||||
from libs.helper import convert_datetime_to_date
|
||||
from libs.helper import convert_datetime_to_date, dump_response
|
||||
from libs.login import login_required
|
||||
from models import AppMode
|
||||
from models.account import Account
|
||||
@ -44,8 +44,15 @@ class DailyMessageStatisticItem(ResponseModel):
|
||||
message_count: int
|
||||
|
||||
|
||||
class DailyMessageStatisticResponse(ResponseModel):
|
||||
data: list[DailyMessageStatisticItem]
|
||||
register_schema_models(console_ns, StatisticTimeRangeQuery)
|
||||
|
||||
|
||||
class StatisticDataResponse[T](ResponseModel):
|
||||
data: list[T]
|
||||
|
||||
|
||||
class DailyMessageStatisticResponse(StatisticDataResponse[DailyMessageStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class DailyConversationStatisticItem(ResponseModel):
|
||||
@ -53,8 +60,8 @@ class DailyConversationStatisticItem(ResponseModel):
|
||||
conversation_count: int
|
||||
|
||||
|
||||
class DailyConversationStatisticResponse(ResponseModel):
|
||||
data: list[DailyConversationStatisticItem]
|
||||
class DailyConversationStatisticResponse(StatisticDataResponse[DailyConversationStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class DailyTerminalStatisticItem(ResponseModel):
|
||||
@ -62,19 +69,19 @@ class DailyTerminalStatisticItem(ResponseModel):
|
||||
terminal_count: int
|
||||
|
||||
|
||||
class DailyTerminalStatisticResponse(ResponseModel):
|
||||
data: list[DailyTerminalStatisticItem]
|
||||
class DailyTerminalStatisticResponse(StatisticDataResponse[DailyTerminalStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class DailyTokenCostStatisticItem(ResponseModel):
|
||||
date: str
|
||||
token_count: int
|
||||
total_price: str | float
|
||||
currency: str
|
||||
token_count: int | None = None
|
||||
total_price: Decimal | None = None
|
||||
currency: str | None = None
|
||||
|
||||
|
||||
class DailyTokenCostStatisticResponse(ResponseModel):
|
||||
data: list[DailyTokenCostStatisticItem]
|
||||
class DailyTokenCostStatisticResponse(StatisticDataResponse[DailyTokenCostStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class AverageSessionInteractionStatisticItem(ResponseModel):
|
||||
@ -82,8 +89,8 @@ class AverageSessionInteractionStatisticItem(ResponseModel):
|
||||
interactions: float
|
||||
|
||||
|
||||
class AverageSessionInteractionStatisticResponse(ResponseModel):
|
||||
data: list[AverageSessionInteractionStatisticItem]
|
||||
class AverageSessionInteractionStatisticResponse(StatisticDataResponse[AverageSessionInteractionStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class UserSatisfactionRateStatisticItem(ResponseModel):
|
||||
@ -91,8 +98,8 @@ class UserSatisfactionRateStatisticItem(ResponseModel):
|
||||
rate: float
|
||||
|
||||
|
||||
class UserSatisfactionRateStatisticResponse(ResponseModel):
|
||||
data: list[UserSatisfactionRateStatisticItem]
|
||||
class UserSatisfactionRateStatisticResponse(StatisticDataResponse[UserSatisfactionRateStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class AverageResponseTimeStatisticItem(ResponseModel):
|
||||
@ -100,8 +107,8 @@ class AverageResponseTimeStatisticItem(ResponseModel):
|
||||
latency: float
|
||||
|
||||
|
||||
class AverageResponseTimeStatisticResponse(ResponseModel):
|
||||
data: list[AverageResponseTimeStatisticItem]
|
||||
class AverageResponseTimeStatisticResponse(StatisticDataResponse[AverageResponseTimeStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
class TokensPerSecondStatisticItem(ResponseModel):
|
||||
@ -109,20 +116,27 @@ class TokensPerSecondStatisticItem(ResponseModel):
|
||||
tps: float
|
||||
|
||||
|
||||
class TokensPerSecondStatisticResponse(ResponseModel):
|
||||
data: list[TokensPerSecondStatisticItem]
|
||||
class TokensPerSecondStatisticResponse(StatisticDataResponse[TokensPerSecondStatisticItem]):
|
||||
pass
|
||||
|
||||
|
||||
register_schema_models(console_ns, StatisticTimeRangeQuery)
|
||||
register_response_schema_models(
|
||||
console_ns,
|
||||
DailyMessageStatisticItem,
|
||||
DailyMessageStatisticResponse,
|
||||
DailyConversationStatisticItem,
|
||||
DailyConversationStatisticResponse,
|
||||
DailyTerminalStatisticItem,
|
||||
DailyTerminalStatisticResponse,
|
||||
DailyTokenCostStatisticItem,
|
||||
DailyTokenCostStatisticResponse,
|
||||
AverageSessionInteractionStatisticItem,
|
||||
AverageSessionInteractionStatisticResponse,
|
||||
UserSatisfactionRateStatisticItem,
|
||||
UserSatisfactionRateStatisticResponse,
|
||||
AverageResponseTimeStatisticItem,
|
||||
AverageResponseTimeStatisticResponse,
|
||||
TokensPerSecondStatisticItem,
|
||||
TokensPerSecondStatisticResponse,
|
||||
)
|
||||
|
||||
@ -131,8 +145,7 @@ register_response_schema_models(
|
||||
class DailyMessageStatistic(Resource):
|
||||
@console_ns.doc("get_daily_message_statistics")
|
||||
@console_ns.doc(description="Get daily message statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Daily message statistics retrieved successfully",
|
||||
@ -185,15 +198,14 @@ WHERE
|
||||
for i in rs:
|
||||
response_data.append({"date": str(i.date), "message_count": i.message_count})
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(DailyMessageStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/daily-conversations")
|
||||
class DailyConversationStatistic(Resource):
|
||||
@console_ns.doc("get_daily_conversation_statistics")
|
||||
@console_ns.doc(description="Get daily conversation statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Daily conversation statistics retrieved successfully",
|
||||
@ -245,15 +257,14 @@ WHERE
|
||||
for i in rs:
|
||||
response_data.append({"date": str(i.date), "conversation_count": i.conversation_count})
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(DailyConversationStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/daily-end-users")
|
||||
class DailyTerminalsStatistic(Resource):
|
||||
@console_ns.doc("get_daily_terminals_statistics")
|
||||
@console_ns.doc(description="Get daily terminal/end-user statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Daily terminal statistics retrieved successfully",
|
||||
@ -306,15 +317,14 @@ WHERE
|
||||
for i in rs:
|
||||
response_data.append({"date": str(i.date), "terminal_count": i.terminal_count})
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(DailyTerminalStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/token-costs")
|
||||
class DailyTokenCostStatistic(Resource):
|
||||
@console_ns.doc("get_daily_token_cost_statistics")
|
||||
@console_ns.doc(description="Get daily token cost statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Daily token cost statistics retrieved successfully",
|
||||
@ -370,15 +380,14 @@ WHERE
|
||||
{"date": str(i.date), "token_count": i.token_count, "total_price": i.total_price, "currency": "USD"}
|
||||
)
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(DailyTokenCostStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/average-session-interactions")
|
||||
class AverageSessionInteractionStatistic(Resource):
|
||||
@console_ns.doc("get_average_session_interaction_statistics")
|
||||
@console_ns.doc(description="Get average session interaction statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Average session interaction statistics retrieved successfully",
|
||||
@ -450,15 +459,14 @@ ORDER BY
|
||||
{"date": str(i.date), "interactions": float(i.interactions.quantize(Decimal("0.01")))}
|
||||
)
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(AverageSessionInteractionStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/user-satisfaction-rate")
|
||||
class UserSatisfactionRateStatistic(Resource):
|
||||
@console_ns.doc("get_user_satisfaction_rate_statistics")
|
||||
@console_ns.doc(description="Get user satisfaction rate statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"User satisfaction rate statistics retrieved successfully",
|
||||
@ -520,15 +528,14 @@ WHERE
|
||||
}
|
||||
)
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(UserSatisfactionRateStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/average-response-time")
|
||||
class AverageResponseTimeStatistic(Resource):
|
||||
@console_ns.doc("get_average_response_time_statistics")
|
||||
@console_ns.doc(description="Get average response time statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Average response time statistics retrieved successfully",
|
||||
@ -581,15 +588,14 @@ WHERE
|
||||
for i in rs:
|
||||
response_data.append({"date": str(i.date), "latency": round(i.latency * 1000, 4)})
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(AverageResponseTimeStatisticResponse, {"data": response_data})
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/statistics/tokens-per-second")
|
||||
class TokensPerSecondStatistic(Resource):
|
||||
@console_ns.doc("get_tokens_per_second_statistics")
|
||||
@console_ns.doc(description="Get tokens per second statistics for an application")
|
||||
@console_ns.doc(params={"app_id": "Application ID"})
|
||||
@console_ns.doc(params=query_params_from_model(StatisticTimeRangeQuery))
|
||||
@console_ns.doc(params={"app_id": "Application ID", **query_params_from_model(StatisticTimeRangeQuery)})
|
||||
@console_ns.response(
|
||||
200,
|
||||
"Tokens per second statistics retrieved successfully",
|
||||
@ -645,4 +651,4 @@ WHERE
|
||||
for i in rs:
|
||||
response_data.append({"date": str(i.date), "tps": round(i.tokens_per_second, 4)})
|
||||
|
||||
return jsonify({"data": response_data})
|
||||
return dump_response(TokensPerSecondStatisticResponse, {"data": response_data})
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
@ -29,6 +30,15 @@ class AnnotationList(ResponseModel):
|
||||
page: int
|
||||
|
||||
|
||||
class AnnotationJobStatusResponse(ResponseModel):
|
||||
job_id: str
|
||||
job_status: Literal["waiting", "processing", "completed", "error"] | str
|
||||
|
||||
|
||||
class AnnotationJobStatusDetailResponse(AnnotationJobStatusResponse):
|
||||
error_msg: str = ""
|
||||
|
||||
|
||||
class AnnotationExportList(ResponseModel):
|
||||
data: list[Annotation]
|
||||
|
||||
|
||||
@ -3,25 +3,14 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from flask_restx import fields
|
||||
from pydantic import field_validator
|
||||
|
||||
from fields.base import ResponseModel
|
||||
from graphon.variables.types import SegmentType
|
||||
from libs.helper import TimestampField, to_timestamp
|
||||
from libs.helper import to_timestamp
|
||||
|
||||
from ._value_type_serializer import serialize_value_type
|
||||
|
||||
conversation_variable_fields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"value_type": fields.String(attribute=serialize_value_type),
|
||||
"value": fields.String,
|
||||
"description": fields.String,
|
||||
"created_at": TimestampField,
|
||||
"updated_at": TimestampField,
|
||||
}
|
||||
|
||||
|
||||
class ConversationVariableResponse(ResponseModel):
|
||||
id: str
|
||||
|
||||
@ -2371,7 +2371,7 @@ Get status of annotation reply action job
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Job status retrieved successfully | **application/json**: [AnnotationJobStatusResponse](#annotationjobstatusresponse)<br> |
|
||||
| 200 | Job status retrieved successfully | **application/json**: [AnnotationJobStatusDetailResponse](#annotationjobstatusdetailresponse)<br> |
|
||||
| 403 | Insufficient permissions | |
|
||||
|
||||
### [GET] /apps/{app_id}/annotation-setting
|
||||
@ -2480,7 +2480,7 @@ Batch import annotations from CSV file with rate limiting and security checks
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Batch import started successfully | **application/json**: [AnnotationJobStatusResponse](#annotationjobstatusresponse)<br> |
|
||||
| 200 | Batch import started successfully | **application/json**: [AnnotationBatchImportResponse](#annotationbatchimportresponse)<br> |
|
||||
| 400 | No file uploaded or too many files | |
|
||||
| 403 | Insufficient permissions | |
|
||||
| 413 | File too large | |
|
||||
@ -2500,7 +2500,7 @@ Get status of batch import job
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Job status retrieved successfully | **application/json**: [AnnotationJobStatusResponse](#annotationjobstatusresponse)<br> |
|
||||
| 200 | Job status retrieved successfully | **application/json**: [AnnotationJobStatusDetailResponse](#annotationjobstatusdetailresponse)<br> |
|
||||
| 403 | Insufficient permissions | |
|
||||
|
||||
### [GET] /apps/{app_id}/annotations/count
|
||||
@ -2816,11 +2816,11 @@ Generate completion message for debugging
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Completion generated successfully | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br> |
|
||||
| 400 | Invalid request parameters | |
|
||||
| 404 | App not found | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Completion generated successfully |
|
||||
| 400 | Invalid request parameters |
|
||||
| 404 | App not found |
|
||||
|
||||
### [POST] /apps/{app_id}/completion-messages/{task_id}/stop
|
||||
Stop a running completion message generation
|
||||
@ -3379,10 +3379,10 @@ Convert text to speech for chat messages
|
||||
|
||||
#### Responses
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Text to speech conversion successful | **application/json**: [AudioBinaryResponse](#audiobinaryresponse)<br> |
|
||||
| 400 | Bad request - Invalid parameters | |
|
||||
| Code | Description |
|
||||
| ---- | ----------- |
|
||||
| 200 | Text to speech conversion successful |
|
||||
| 400 | Bad request - Invalid parameters |
|
||||
|
||||
### [GET] /apps/{app_id}/text-to-audio/voices
|
||||
Get available TTS voices for a specific language
|
||||
@ -14644,19 +14644,21 @@ Soft lifecycle state for Agent records.
|
||||
| id | string | | Yes |
|
||||
| question | string | | No |
|
||||
|
||||
#### AnnotationBatchImportResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| error_msg | string | | No |
|
||||
| job_id | string | | No |
|
||||
| job_status | string | | No |
|
||||
| record_count | integer | | No |
|
||||
|
||||
#### AnnotationCountResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| count | integer | Number of annotations | Yes |
|
||||
|
||||
#### AnnotationEmbeddingModelResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| embedding_model_name | string | | No |
|
||||
| embedding_provider_name | string | | No |
|
||||
|
||||
#### AnnotationExportList
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -14698,14 +14700,20 @@ Soft lifecycle state for Agent records.
|
||||
| limit | integer, <br>**Default:** 20 | Page size | No |
|
||||
| page | integer, <br>**Default:** 1 | Page number | No |
|
||||
|
||||
#### AnnotationJobStatusResponse
|
||||
#### AnnotationJobStatusDetailResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| error_msg | string | | No |
|
||||
| job_id | string | | No |
|
||||
| job_status | string | | No |
|
||||
| record_count | integer | | No |
|
||||
| job_id | string | | Yes |
|
||||
| job_status | string<br>string | | Yes |
|
||||
|
||||
#### AnnotationJobStatusResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| job_id | string | | Yes |
|
||||
| job_status | string<br>string | | Yes |
|
||||
|
||||
#### AnnotationList
|
||||
|
||||
@ -14739,11 +14747,18 @@ Soft lifecycle state for Agent records.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| action | string, <br>**Available values:** "disable", "enable" | *Enum:* `"disable"`, `"enable"` | Yes |
|
||||
|
||||
#### AnnotationSettingEmbeddingModelResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| embedding_model_name | string | | No |
|
||||
| embedding_provider_name | string | | No |
|
||||
|
||||
#### AnnotationSettingResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| embedding_model | [AnnotationEmbeddingModelResponse](#annotationembeddingmodelresponse) | | No |
|
||||
| embedding_model | [AnnotationSettingEmbeddingModelResponse](#annotationsettingembeddingmodelresponse) | | No |
|
||||
| enabled | boolean | | Yes |
|
||||
| id | string | | No |
|
||||
| score_threshold | number | | No |
|
||||
@ -15712,13 +15727,13 @@ Enum class for configurate method of provider model.
|
||||
| admin_feedback_stats | [FeedbackStat](#feedbackstat) | | No |
|
||||
| annotation | [ConversationAnnotation](#conversationannotation) | | No |
|
||||
| created_at | integer | | No |
|
||||
| first_message | [SimpleMessageDetail](#simplemessagedetail) | | No |
|
||||
| from_account_id | string | | No |
|
||||
| from_account_name | string | | No |
|
||||
| from_end_user_id | string | | No |
|
||||
| from_end_user_session_id | string | | No |
|
||||
| from_source | string | | Yes |
|
||||
| id | string | | Yes |
|
||||
| message | [SimpleMessageDetail](#simplemessagedetail) | | No |
|
||||
| model_config | [SimpleModelConfig](#simplemodelconfig) | | No |
|
||||
| read_at | integer | | No |
|
||||
| status | string | | Yes |
|
||||
@ -15782,11 +15797,11 @@ Enum class for configurate method of provider model.
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| created_at | integer | | No |
|
||||
| first_message | [MessageDetail](#messagedetail) | | No |
|
||||
| from_account_id | string | | No |
|
||||
| from_end_user_id | string | | No |
|
||||
| from_source | string | | Yes |
|
||||
| id | string | | Yes |
|
||||
| message | [MessageDetail](#messagedetail) | | No |
|
||||
| model_config | [ModelConfig](#modelconfig) | | No |
|
||||
| status | string | | Yes |
|
||||
|
||||
@ -15794,10 +15809,10 @@ Enum class for configurate method of provider model.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| has_next | boolean | | Yes |
|
||||
| items | [ [Conversation](#conversation) ] | | Yes |
|
||||
| data | [ [Conversation](#conversation) ] | | Yes |
|
||||
| has_more | boolean | | Yes |
|
||||
| limit | integer | | Yes |
|
||||
| page | integer | | Yes |
|
||||
| per_page | integer | | Yes |
|
||||
| total | integer | | Yes |
|
||||
|
||||
#### ConversationRenamePayload
|
||||
@ -15860,7 +15875,7 @@ Enum class for configurate method of provider model.
|
||||
| read_at | integer | | No |
|
||||
| status | string | | Yes |
|
||||
| status_count | [StatusCount](#statuscount) | | No |
|
||||
| summary_or_query | string | | Yes |
|
||||
| summary | string | | Yes |
|
||||
| updated_at | integer | | No |
|
||||
| user_feedback_stats | [FeedbackStat](#feedbackstat) | | No |
|
||||
|
||||
@ -15868,10 +15883,10 @@ Enum class for configurate method of provider model.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| has_next | boolean | | Yes |
|
||||
| items | [ [ConversationWithSummary](#conversationwithsummary) ] | | Yes |
|
||||
| data | [ [ConversationWithSummary](#conversationwithsummary) ] | | Yes |
|
||||
| has_more | boolean | | Yes |
|
||||
| limit | integer | | Yes |
|
||||
| page | integer | | Yes |
|
||||
| per_page | integer | | Yes |
|
||||
| total | integer | | Yes |
|
||||
|
||||
#### ConvertToWorkflowPayload
|
||||
@ -16044,10 +16059,10 @@ Model class for provider custom model configuration.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| currency | string | | Yes |
|
||||
| currency | string | | No |
|
||||
| date | string | | Yes |
|
||||
| token_count | integer | | Yes |
|
||||
| total_price | string<br>number | | Yes |
|
||||
| token_count | integer | | No |
|
||||
| total_price | string | | No |
|
||||
|
||||
#### DailyTokenCostStatisticResponse
|
||||
|
||||
@ -18344,6 +18359,7 @@ Enum class for large language model mode.
|
||||
| agent_thoughts | [ [AgentThought](#agentthought) ] | | Yes |
|
||||
| annotation | [ConversationAnnotation](#conversationannotation) | | No |
|
||||
| annotation_hit_history | [ConversationAnnotationHitHistory](#conversationannotationhithistory) | | No |
|
||||
| answer | string | | Yes |
|
||||
| answer_tokens | integer | | Yes |
|
||||
| conversation_id | string | | Yes |
|
||||
| created_at | integer | | No |
|
||||
@ -18356,12 +18372,11 @@ Enum class for large language model mode.
|
||||
| inputs | object | | Yes |
|
||||
| message | [JSONValue](#jsonvalue) | | Yes |
|
||||
| message_files | [ [MessageFile](#messagefile) ] | | Yes |
|
||||
| message_metadata_dict | [JSONValue](#jsonvalue) | | Yes |
|
||||
| message_tokens | integer | | Yes |
|
||||
| metadata | [JSONValue](#jsonvalue) | | Yes |
|
||||
| parent_message_id | string | | No |
|
||||
| provider_response_latency | number | | Yes |
|
||||
| query | string | | Yes |
|
||||
| re_sign_file_url_answer | string | | Yes |
|
||||
| status | string | | Yes |
|
||||
| workflow_run_id | string | | No |
|
||||
|
||||
@ -18369,27 +18384,27 @@ Enum class for large language model mode.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| agent_thoughts | [ [AgentThought](#agentthought) ] | | No |
|
||||
| agent_thoughts | [ [AgentThought](#agentthought) ] | | Yes |
|
||||
| annotation | [ConversationAnnotation](#conversationannotation) | | No |
|
||||
| annotation_hit_history | [ConversationAnnotationHitHistory](#conversationannotationhithistory) | | No |
|
||||
| answer | string | | Yes |
|
||||
| answer_tokens | integer | | No |
|
||||
| answer_tokens | integer | | Yes |
|
||||
| conversation_id | string | | Yes |
|
||||
| created_at | integer | | No |
|
||||
| error | string | | No |
|
||||
| extra_contents | [ [HumanInputContent](#humaninputcontent) ] | | No |
|
||||
| feedbacks | [ [Feedback](#feedback) ] | | No |
|
||||
| feedbacks | [ [Feedback](#feedback) ] | | Yes |
|
||||
| from_account_id | string | | No |
|
||||
| from_end_user_id | string | | No |
|
||||
| from_source | string | | Yes |
|
||||
| id | string | | Yes |
|
||||
| inputs | object | | Yes |
|
||||
| message | [JSONValue](#jsonvalue) | | No |
|
||||
| message_files | [ [MessageFile](#messagefile) ] | | No |
|
||||
| message_tokens | integer | | No |
|
||||
| metadata | [JSONValue](#jsonvalue) | | No |
|
||||
| message | [JSONValue](#jsonvalue) | | Yes |
|
||||
| message_files | [ [MessageFile](#messagefile) ] | | Yes |
|
||||
| message_tokens | integer | | Yes |
|
||||
| metadata | [JSONValue](#jsonvalue) | | Yes |
|
||||
| parent_message_id | string | | No |
|
||||
| provider_response_latency | number | | No |
|
||||
| provider_response_latency | number | | Yes |
|
||||
| query | string | | Yes |
|
||||
| status | string | | Yes |
|
||||
| workflow_run_id | string | | No |
|
||||
@ -20612,7 +20627,7 @@ Whitelist scopes accepted by RBAC app and dataset access config APIs.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| model_dict | [JSONValue](#jsonvalue) | | No |
|
||||
| model | [JSONValue](#jsonvalue) | | No |
|
||||
| pre_prompt | string | | No |
|
||||
|
||||
#### SimpleProviderEntityResponse
|
||||
@ -21235,9 +21250,11 @@ Tag type
|
||||
|
||||
#### TextToSpeechVoiceListResponse
|
||||
|
||||
Available voices
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| TextToSpeechVoiceListResponse | array | | |
|
||||
| TextToSpeechVoiceListResponse | array | Available voices | |
|
||||
|
||||
#### TextToSpeechVoiceQuery
|
||||
|
||||
@ -21245,6 +21262,13 @@ Tag type
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| language | string | Language code | Yes |
|
||||
|
||||
#### TextToSpeechVoiceResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| name | string | Voice display name | Yes |
|
||||
| value | string | Voice identifier | Yes |
|
||||
|
||||
#### TokensPerSecondStatisticItem
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|
||||
@ -148,7 +148,8 @@ def test_console_text_api_error_mapping(app: Flask, monkeypatch: pytest.MonkeyPa
|
||||
|
||||
|
||||
def test_console_text_modes_success(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(AudioService, "transcript_tts_voices", lambda **_kwargs: ["voice-1"])
|
||||
expected_voices = [{"name": "Voice 1", "value": "voice-1"}]
|
||||
monkeypatch.setattr(AudioService, "transcript_tts_voices", lambda **_kwargs: expected_voices)
|
||||
|
||||
api = TextModesApi()
|
||||
handler = unwrap(api.get)
|
||||
@ -157,7 +158,7 @@ def test_console_text_modes_success(app: Flask, monkeypatch: pytest.MonkeyPatch)
|
||||
with app.test_request_context("/console/api/apps/app/text-to-audio/voices?language=en", method="GET"):
|
||||
response = handler(api, app_model=app_model)
|
||||
|
||||
assert response == ["voice-1"]
|
||||
assert response == expected_voices
|
||||
|
||||
|
||||
def test_console_text_modes_language_error(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -242,7 +243,8 @@ def test_text_to_audio_voices_success(app: Flask, monkeypatch: pytest.MonkeyPatc
|
||||
api = TextModesApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
monkeypatch.setattr(AudioService, "transcript_tts_voices", lambda **_kwargs: ["voice-1"])
|
||||
expected_voices = [{"name": "Voice 1", "value": "voice-1"}]
|
||||
monkeypatch.setattr(AudioService, "transcript_tts_voices", lambda **_kwargs: expected_voices)
|
||||
|
||||
app_model = SimpleNamespace(tenant_id="tenant-1")
|
||||
|
||||
@ -253,7 +255,7 @@ def test_text_to_audio_voices_success(app: Flask, monkeypatch: pytest.MonkeyPatc
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
|
||||
assert response == ["voice-1"]
|
||||
assert response == expected_voices
|
||||
|
||||
|
||||
def test_audio_to_text_with_invalid_file(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -300,7 +302,7 @@ def test_text_to_audio_voices_with_language_filter(app: Flask, monkeypatch: pyte
|
||||
monkeypatch.setattr(
|
||||
AudioService,
|
||||
"transcript_tts_voices",
|
||||
lambda **_kwargs: [{"id": "voice-1", "name": "Voice 1"}],
|
||||
lambda **_kwargs: [{"name": "Voice 1", "value": "voice-1"}],
|
||||
)
|
||||
|
||||
app_model = SimpleNamespace(tenant_id="tenant-1")
|
||||
|
||||
@ -1,149 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
from inspect import unwrap
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from controllers.console.app import audio as audio_module
|
||||
from controllers.console.app.error import AudioTooLargeError
|
||||
from services.errors.audio import AudioTooLargeServiceError
|
||||
|
||||
|
||||
def test_audio_to_text_success(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.ChatMessageAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
response_payload = {"text": "hello"}
|
||||
monkeypatch.setattr(audio_module.AudioService, "transcript_asr", lambda **_kwargs: response_payload)
|
||||
|
||||
app_model = SimpleNamespace(id="app-1")
|
||||
|
||||
data = {"file": (io.BytesIO(b"x"), "sample.wav")}
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/audio-to-text",
|
||||
method="POST",
|
||||
data=data,
|
||||
content_type="multipart/form-data",
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
|
||||
assert response == response_payload
|
||||
|
||||
|
||||
def test_audio_to_text_maps_audio_too_large(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.ChatMessageAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
monkeypatch.setattr(
|
||||
audio_module.AudioService,
|
||||
"transcript_asr",
|
||||
lambda **_kwargs: (_ for _ in ()).throw(AudioTooLargeServiceError("too large")),
|
||||
)
|
||||
|
||||
app_model = SimpleNamespace(id="app-1")
|
||||
|
||||
data = {"file": (io.BytesIO(b"x"), "sample.wav")}
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/audio-to-text",
|
||||
method="POST",
|
||||
data=data,
|
||||
content_type="multipart/form-data",
|
||||
):
|
||||
with pytest.raises(AudioTooLargeError):
|
||||
method(api, app_model=app_model)
|
||||
|
||||
|
||||
def test_text_to_audio_success(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.ChatMessageTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
monkeypatch.setattr(audio_module.AudioService, "transcript_tts", lambda **_kwargs: {"audio": "ok"})
|
||||
|
||||
app_model = SimpleNamespace(id="app-1")
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/text-to-audio",
|
||||
method="POST",
|
||||
json={"text": "hello"},
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
|
||||
assert response == {"audio": "ok"}
|
||||
|
||||
|
||||
def test_text_to_audio_voices_success(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.TextModesApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
monkeypatch.setattr(audio_module.AudioService, "transcript_tts_voices", lambda **_kwargs: ["voice-1"])
|
||||
|
||||
app_model = SimpleNamespace(tenant_id="tenant-1")
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/text-to-audio/voices",
|
||||
method="GET",
|
||||
query_string={"language": "en-US"},
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
|
||||
assert response == ["voice-1"]
|
||||
|
||||
|
||||
def test_audio_to_text_with_invalid_file(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.ChatMessageAudioApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
monkeypatch.setattr(audio_module.AudioService, "transcript_asr", lambda **_kwargs: {"text": "test"})
|
||||
|
||||
app_model = SimpleNamespace(id="app-1")
|
||||
|
||||
data = {"file": (io.BytesIO(b"invalid"), "sample.xyz")}
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/audio-to-text",
|
||||
method="POST",
|
||||
data=data,
|
||||
content_type="multipart/form-data",
|
||||
):
|
||||
# Should not raise, AudioService is mocked
|
||||
response = method(api, app_model=app_model)
|
||||
assert response == {"text": "test"}
|
||||
|
||||
|
||||
def test_text_to_audio_with_language_param(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.ChatMessageTextApi()
|
||||
method = unwrap(api.post)
|
||||
|
||||
monkeypatch.setattr(audio_module.AudioService, "transcript_tts", lambda **_kwargs: {"audio": "test"})
|
||||
|
||||
app_model = SimpleNamespace(id="app-1")
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/text-to-audio",
|
||||
method="POST",
|
||||
json={"text": "hello", "language": "en-US"},
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
assert response == {"audio": "test"}
|
||||
|
||||
|
||||
def test_text_to_audio_voices_with_language_filter(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = audio_module.TextModesApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
monkeypatch.setattr(
|
||||
audio_module.AudioService,
|
||||
"transcript_tts_voices",
|
||||
lambda **_kwargs: [{"id": "voice-1", "name": "Voice 1"}],
|
||||
)
|
||||
|
||||
app_model = SimpleNamespace(tenant_id="tenant-1")
|
||||
|
||||
with app.test_request_context(
|
||||
"/console/api/apps/app-1/text-to-audio/voices?language=en-US",
|
||||
method="GET",
|
||||
):
|
||||
response = method(api, app_model=app_model)
|
||||
assert isinstance(response, list)
|
||||
@ -125,13 +125,57 @@ def test_message_detail_response_normalizes_aliases_and_timestamp(app: Flask, mo
|
||||
"conversation_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"inputs": {"foo": "bar"},
|
||||
"query": "hello",
|
||||
"re_sign_file_url_answer": "world",
|
||||
"message": [{"text": "hello"}],
|
||||
"message_tokens": 7,
|
||||
"answer": "world",
|
||||
"answer_tokens": 11,
|
||||
"provider_response_latency": 1.25,
|
||||
"from_source": "user",
|
||||
"from_end_user_id": None,
|
||||
"from_account_id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"feedbacks": [],
|
||||
"workflow_run_id": None,
|
||||
"annotation": None,
|
||||
"annotation_hit_history": None,
|
||||
"status": "normal",
|
||||
"created_at": created_at,
|
||||
"agent_thoughts": [],
|
||||
"message_files": [],
|
||||
"message_metadata_dict": {"token_usage": 3},
|
||||
"error": None,
|
||||
"parent_message_id": None,
|
||||
"extra_contents": [],
|
||||
}
|
||||
)
|
||||
assert response.answer == "world"
|
||||
assert response.message_tokens == 7
|
||||
assert response.answer_tokens == 11
|
||||
assert response.provider_response_latency == 1.25
|
||||
assert response.metadata == {"token_usage": 3}
|
||||
assert response.created_at == int(created_at.timestamp())
|
||||
assert response.model_dump(mode="json") == {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"conversation_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"inputs": {"foo": "bar"},
|
||||
"query": "hello",
|
||||
"message": [{"text": "hello"}],
|
||||
"message_tokens": 7,
|
||||
"answer": "world",
|
||||
"answer_tokens": 11,
|
||||
"provider_response_latency": 1.25,
|
||||
"from_source": "user",
|
||||
"from_end_user_id": None,
|
||||
"from_account_id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"feedbacks": [],
|
||||
"workflow_run_id": None,
|
||||
"annotation": None,
|
||||
"annotation_hit_history": None,
|
||||
"created_at": int(created_at.timestamp()),
|
||||
"agent_thoughts": [],
|
||||
"message_files": [],
|
||||
"metadata": {"token_usage": 3},
|
||||
"status": "normal",
|
||||
"error": None,
|
||||
"parent_message_id": None,
|
||||
"extra_contents": [],
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from decimal import Decimal
|
||||
from inspect import unwrap
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
@ -39,6 +40,10 @@ def _install_common(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(statistic_module, "convert_datetime_to_date", lambda field: field)
|
||||
|
||||
|
||||
def _json_payload(response: Any) -> dict[str, Any]:
|
||||
return response if isinstance(response, dict) else response.get_json()
|
||||
|
||||
|
||||
def test_daily_message_statistic_returns_rows(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
api = statistic_module.DailyMessageStatistic()
|
||||
method = unwrap(api.get)
|
||||
@ -50,7 +55,7 @@ def test_daily_message_statistic_returns_rows(app: Flask, monkeypatch: pytest.Mo
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-messages", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
assert response.get_json() == {"data": [{"date": "2024-01-01", "message_count": 3}]}
|
||||
assert _json_payload(response) == {"data": [{"date": "2024-01-01", "message_count": 3}]}
|
||||
|
||||
|
||||
def test_daily_conversation_statistic_returns_rows(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -64,7 +69,7 @@ def test_daily_conversation_statistic_returns_rows(app: Flask, monkeypatch: pyte
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-conversations", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
assert response.get_json() == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
|
||||
assert _json_payload(response) == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
|
||||
|
||||
|
||||
def test_daily_token_cost_statistic_returns_rows(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -78,11 +83,11 @@ def test_daily_token_cost_statistic_returns_rows(app: Flask, monkeypatch: pytest
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/token-costs", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
data = response.get_json()
|
||||
data = _json_payload(response)
|
||||
assert len(data["data"]) == 1
|
||||
assert data["data"][0]["date"] == "2024-01-03"
|
||||
assert data["data"][0]["token_count"] == 10
|
||||
assert data["data"][0]["total_price"] == 0.25
|
||||
assert Decimal(data["data"][0]["total_price"]) == Decimal("0.25")
|
||||
|
||||
|
||||
def test_daily_terminals_statistic_returns_rows(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -96,7 +101,7 @@ def test_daily_terminals_statistic_returns_rows(app: Flask, monkeypatch: pytest.
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-end-users", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
assert response.get_json() == {"data": [{"date": "2024-01-04", "terminal_count": 7}]}
|
||||
assert _json_payload(response) == {"data": [{"date": "2024-01-04", "terminal_count": 7}]}
|
||||
|
||||
|
||||
def test_average_session_interaction_statistic_requires_chat_mode(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -139,7 +144,7 @@ def test_daily_message_statistic_multiple_rows(app: Flask, monkeypatch: pytest.M
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-messages", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
data = response.get_json()
|
||||
data = _json_payload(response)
|
||||
assert len(data["data"]) == 3
|
||||
|
||||
|
||||
@ -153,7 +158,7 @@ def test_daily_message_statistic_empty_result(app: Flask, monkeypatch: pytest.Mo
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-messages", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
assert response.get_json() == {"data": []}
|
||||
assert _json_payload(response) == {"data": []}
|
||||
|
||||
|
||||
def test_daily_conversation_statistic_with_time_range(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -172,7 +177,7 @@ def test_daily_conversation_statistic_with_time_range(app: Flask, monkeypatch: p
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/daily-conversations", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
assert response.get_json() == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
|
||||
assert _json_payload(response) == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
|
||||
|
||||
|
||||
def test_daily_token_cost_with_multiple_currencies(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
@ -189,5 +194,5 @@ def test_daily_token_cost_with_multiple_currencies(app: Flask, monkeypatch: pyte
|
||||
with app.test_request_context("/console/api/apps/app-1/statistics/token-costs", method="GET"):
|
||||
response = method(api, SimpleNamespace(timezone="UTC"), app_model=SimpleNamespace(id="app-1"))
|
||||
|
||||
data = response.get_json()
|
||||
data = _json_payload(response)
|
||||
assert len(data["data"]) == 2
|
||||
|
||||
@ -378,16 +378,16 @@ export type AgentLogMessageListResponse = {
|
||||
}
|
||||
|
||||
export type MessageDetailResponse = {
|
||||
agent_thoughts?: Array<AgentThought>
|
||||
agent_thoughts: Array<AgentThought>
|
||||
annotation?: ConversationAnnotation | null
|
||||
annotation_hit_history?: ConversationAnnotationHitHistory | null
|
||||
answer: string
|
||||
answer_tokens?: number | null
|
||||
answer_tokens: number
|
||||
conversation_id: string
|
||||
created_at?: number | null
|
||||
error?: string | null
|
||||
extra_contents?: Array<HumanInputContent>
|
||||
feedbacks?: Array<Feedback>
|
||||
feedbacks: Array<Feedback>
|
||||
from_account_id?: string | null
|
||||
from_end_user_id?: string | null
|
||||
from_source: string
|
||||
@ -395,12 +395,12 @@ export type MessageDetailResponse = {
|
||||
inputs: {
|
||||
[key: string]: JsonValue
|
||||
}
|
||||
message?: JsonValue | null
|
||||
message_files?: Array<MessageFile>
|
||||
message_tokens?: number | null
|
||||
metadata?: JsonValue | null
|
||||
message: JsonValue
|
||||
message_files: Array<MessageFile>
|
||||
message_tokens: number
|
||||
metadata: JsonValue
|
||||
parent_message_id?: string | null
|
||||
provider_response_latency?: number | null
|
||||
provider_response_latency: number
|
||||
query: string
|
||||
status: string
|
||||
workflow_run_id?: string | null
|
||||
|
||||
@ -2525,27 +2525,27 @@ export const zHumanInputContent = z.object({
|
||||
* MessageDetailResponse
|
||||
*/
|
||||
export const zMessageDetailResponse = z.object({
|
||||
agent_thoughts: z.array(zAgentThought).optional(),
|
||||
agent_thoughts: z.array(zAgentThought),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
|
||||
answer: z.string(),
|
||||
answer_tokens: z.int().nullish(),
|
||||
answer_tokens: z.int(),
|
||||
conversation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
error: z.string().nullish(),
|
||||
extra_contents: z.array(zHumanInputContent).optional(),
|
||||
feedbacks: z.array(zFeedback).optional(),
|
||||
feedbacks: z.array(zFeedback),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
inputs: z.record(z.string(), zJsonValue),
|
||||
message: zJsonValue.nullish(),
|
||||
message_files: z.array(zMessageFile).optional(),
|
||||
message_tokens: z.int().nullish(),
|
||||
metadata: zJsonValue.nullish(),
|
||||
message: zJsonValue,
|
||||
message_files: z.array(zMessageFile),
|
||||
message_tokens: z.int(),
|
||||
metadata: zJsonValue,
|
||||
parent_message_id: z.string().nullish(),
|
||||
provider_response_latency: z.number().nullish(),
|
||||
provider_response_latency: z.number(),
|
||||
query: z.string(),
|
||||
status: z.string(),
|
||||
workflow_run_id: z.string().nullish(),
|
||||
|
||||
@ -329,14 +329,18 @@ export type AnnotationReplyPayload = {
|
||||
}
|
||||
|
||||
export type AnnotationJobStatusResponse = {
|
||||
error_msg?: string | null
|
||||
job_id?: string | null
|
||||
job_status?: string | null
|
||||
record_count?: number | null
|
||||
job_id: string
|
||||
job_status: 'completed' | 'error' | 'processing' | 'waiting' | string
|
||||
}
|
||||
|
||||
export type AnnotationJobStatusDetailResponse = {
|
||||
error_msg?: string
|
||||
job_id: string
|
||||
job_status: 'completed' | 'error' | 'processing' | 'waiting' | string
|
||||
}
|
||||
|
||||
export type AnnotationSettingResponse = {
|
||||
embedding_model?: AnnotationEmbeddingModelResponse | null
|
||||
embedding_model?: AnnotationSettingEmbeddingModelResponse | null
|
||||
enabled: boolean
|
||||
id?: string | null
|
||||
score_threshold?: number | null
|
||||
@ -372,6 +376,13 @@ export type Annotation = {
|
||||
question?: string | null
|
||||
}
|
||||
|
||||
export type AnnotationBatchImportResponse = {
|
||||
error_msg?: string | null
|
||||
job_id?: string | null
|
||||
job_status?: string | null
|
||||
record_count?: number | null
|
||||
}
|
||||
|
||||
export type AnnotationCountResponse = {
|
||||
count: number
|
||||
}
|
||||
@ -429,10 +440,10 @@ export type AudioTranscriptResponse = {
|
||||
}
|
||||
|
||||
export type ConversationWithSummaryPagination = {
|
||||
has_next: boolean
|
||||
items: Array<ConversationWithSummary>
|
||||
data: Array<ConversationWithSummary>
|
||||
has_more: boolean
|
||||
limit: number
|
||||
page: number
|
||||
per_page: number
|
||||
total: number
|
||||
}
|
||||
|
||||
@ -467,20 +478,20 @@ export type SimpleResultResponse = {
|
||||
}
|
||||
|
||||
export type ConversationPagination = {
|
||||
has_next: boolean
|
||||
items: Array<Conversation>
|
||||
data: Array<Conversation>
|
||||
has_more: boolean
|
||||
limit: number
|
||||
page: number
|
||||
per_page: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export type ConversationMessageDetail = {
|
||||
created_at?: number | null
|
||||
first_message?: MessageDetail | null
|
||||
from_account_id?: string | null
|
||||
from_end_user_id?: string | null
|
||||
from_source: string
|
||||
id: string
|
||||
message?: MessageDetail | null
|
||||
model_config?: ModelConfig | null
|
||||
status: string
|
||||
}
|
||||
@ -555,16 +566,16 @@ export type AppIconPayload = {
|
||||
}
|
||||
|
||||
export type MessageDetailResponse = {
|
||||
agent_thoughts?: Array<AgentThought>
|
||||
agent_thoughts: Array<AgentThought>
|
||||
annotation?: ConversationAnnotation | null
|
||||
annotation_hit_history?: ConversationAnnotationHitHistory | null
|
||||
answer: string
|
||||
answer_tokens?: number | null
|
||||
answer_tokens: number
|
||||
conversation_id: string
|
||||
created_at?: number | null
|
||||
error?: string | null
|
||||
extra_contents?: Array<HumanInputContent>
|
||||
feedbacks?: Array<Feedback>
|
||||
feedbacks: Array<Feedback>
|
||||
from_account_id?: string | null
|
||||
from_end_user_id?: string | null
|
||||
from_source: string
|
||||
@ -572,12 +583,12 @@ export type MessageDetailResponse = {
|
||||
inputs: {
|
||||
[key: string]: JsonValue
|
||||
}
|
||||
message?: JsonValue | null
|
||||
message_files?: Array<MessageFile>
|
||||
message_tokens?: number | null
|
||||
metadata?: JsonValue | null
|
||||
message: JsonValue
|
||||
message_files: Array<MessageFile>
|
||||
message_tokens: number
|
||||
metadata: JsonValue
|
||||
parent_message_id?: string | null
|
||||
provider_response_latency?: number | null
|
||||
provider_response_latency: number
|
||||
query: string
|
||||
status: string
|
||||
workflow_run_id?: string | null
|
||||
@ -736,11 +747,7 @@ export type TextToSpeechPayload = {
|
||||
voice?: string | null
|
||||
}
|
||||
|
||||
export type AudioBinaryResponse = Blob | File
|
||||
|
||||
export type TextToSpeechVoiceListResponse = Array<{
|
||||
[key: string]: unknown
|
||||
}>
|
||||
export type TextToSpeechVoiceListResponse = Array<TextToSpeechVoiceResponse>
|
||||
|
||||
export type AppTraceResponse = {
|
||||
enabled?: boolean
|
||||
@ -1544,7 +1551,7 @@ export type CliToolSuggestion = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export type AnnotationEmbeddingModelResponse = {
|
||||
export type AnnotationSettingEmbeddingModelResponse = {
|
||||
embedding_model_name?: string | null
|
||||
embedding_provider_name?: string | null
|
||||
}
|
||||
@ -1575,7 +1582,7 @@ export type ConversationWithSummary = {
|
||||
read_at?: number | null
|
||||
status: string
|
||||
status_count?: StatusCount | null
|
||||
summary_or_query: string
|
||||
summary: string
|
||||
updated_at?: number | null
|
||||
user_feedback_stats?: FeedbackStat | null
|
||||
}
|
||||
@ -1589,13 +1596,13 @@ export type Conversation = {
|
||||
admin_feedback_stats?: FeedbackStat | null
|
||||
annotation?: ConversationAnnotation | null
|
||||
created_at?: number | null
|
||||
first_message?: SimpleMessageDetail | null
|
||||
from_account_id?: string | null
|
||||
from_account_name?: string | null
|
||||
from_end_user_id?: string | null
|
||||
from_end_user_session_id?: string | null
|
||||
from_source: string
|
||||
id: string
|
||||
message?: SimpleMessageDetail | null
|
||||
model_config?: SimpleModelConfig | null
|
||||
read_at?: number | null
|
||||
status: string
|
||||
@ -1607,6 +1614,7 @@ export type MessageDetail = {
|
||||
agent_thoughts: Array<AgentThought>
|
||||
annotation?: ConversationAnnotation | null
|
||||
annotation_hit_history?: ConversationAnnotationHitHistory | null
|
||||
answer: string
|
||||
answer_tokens: number
|
||||
conversation_id: string
|
||||
created_at?: number | null
|
||||
@ -1621,12 +1629,11 @@ export type MessageDetail = {
|
||||
}
|
||||
message: JsonValue
|
||||
message_files: Array<MessageFile>
|
||||
message_metadata_dict: JsonValue
|
||||
message_tokens: number
|
||||
metadata: JsonValue
|
||||
parent_message_id?: string | null
|
||||
provider_response_latency: number
|
||||
query: string
|
||||
re_sign_file_url_answer: string
|
||||
status: string
|
||||
workflow_run_id?: string | null
|
||||
}
|
||||
@ -1725,10 +1732,10 @@ export type DailyMessageStatisticItem = {
|
||||
}
|
||||
|
||||
export type DailyTokenCostStatisticItem = {
|
||||
currency: string
|
||||
currency?: string | null
|
||||
date: string
|
||||
token_count: number
|
||||
total_price: string | number
|
||||
token_count?: number | null
|
||||
total_price?: string | null
|
||||
}
|
||||
|
||||
export type TokensPerSecondStatisticItem = {
|
||||
@ -1741,6 +1748,11 @@ export type UserSatisfactionRateStatisticItem = {
|
||||
rate: number
|
||||
}
|
||||
|
||||
export type TextToSpeechVoiceResponse = {
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type WorkflowAppLogPartialResponse = {
|
||||
created_at?: number | null
|
||||
created_by_account?: SimpleAccount | null
|
||||
@ -2224,7 +2236,7 @@ export type EnvSuggestion = {
|
||||
}
|
||||
|
||||
export type SimpleModelConfig = {
|
||||
model_dict?: JsonValue | null
|
||||
model?: JsonValue | null
|
||||
pre_prompt?: string | null
|
||||
}
|
||||
|
||||
@ -4045,7 +4057,7 @@ export type GetAppsByAppIdAnnotationReplyByActionStatusByJobIdErrors = {
|
||||
}
|
||||
|
||||
export type GetAppsByAppIdAnnotationReplyByActionStatusByJobIdResponses = {
|
||||
200: AnnotationJobStatusResponse
|
||||
200: AnnotationJobStatusDetailResponse
|
||||
}
|
||||
|
||||
export type GetAppsByAppIdAnnotationReplyByActionStatusByJobIdResponse
|
||||
@ -4169,7 +4181,7 @@ export type PostAppsByAppIdAnnotationsBatchImportErrors = {
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdAnnotationsBatchImportResponses = {
|
||||
200: AnnotationJobStatusResponse
|
||||
200: AnnotationBatchImportResponse
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdAnnotationsBatchImportResponse
|
||||
@ -4190,7 +4202,7 @@ export type GetAppsByAppIdAnnotationsBatchImportStatusByJobIdErrors = {
|
||||
}
|
||||
|
||||
export type GetAppsByAppIdAnnotationsBatchImportStatusByJobIdResponses = {
|
||||
200: AnnotationJobStatusResponse
|
||||
200: AnnotationJobStatusDetailResponse
|
||||
}
|
||||
|
||||
export type GetAppsByAppIdAnnotationsBatchImportStatusByJobIdResponse
|
||||
@ -4556,7 +4568,9 @@ export type PostAppsByAppIdCompletionMessagesErrors = {
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdCompletionMessagesResponses = {
|
||||
200: GeneratedAppResponse
|
||||
200: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdCompletionMessagesResponse
|
||||
@ -5130,7 +5144,9 @@ export type PostAppsByAppIdTextToAudioErrors = {
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdTextToAudioResponses = {
|
||||
200: AudioBinaryResponse
|
||||
200: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type PostAppsByAppIdTextToAudioResponse
|
||||
|
||||
@ -188,10 +188,17 @@ export const zAnnotationReplyPayload = z.object({
|
||||
* AnnotationJobStatusResponse
|
||||
*/
|
||||
export const zAnnotationJobStatusResponse = z.object({
|
||||
error_msg: z.string().nullish(),
|
||||
job_id: z.string().nullish(),
|
||||
job_status: z.string().nullish(),
|
||||
record_count: z.int().nullish(),
|
||||
job_id: z.string(),
|
||||
job_status: z.union([z.enum(['completed', 'error', 'processing', 'waiting']), z.string()]),
|
||||
})
|
||||
|
||||
/**
|
||||
* AnnotationJobStatusDetailResponse
|
||||
*/
|
||||
export const zAnnotationJobStatusDetailResponse = z.object({
|
||||
error_msg: z.string().optional().default(''),
|
||||
job_id: z.string(),
|
||||
job_status: z.union([z.enum(['completed', 'error', 'processing', 'waiting']), z.string()]),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -234,6 +241,16 @@ export const zAnnotationList = z.object({
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AnnotationBatchImportResponse
|
||||
*/
|
||||
export const zAnnotationBatchImportResponse = z.object({
|
||||
error_msg: z.string().nullish(),
|
||||
job_id: z.string().nullish(),
|
||||
job_status: z.string().nullish(),
|
||||
record_count: z.int().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AnnotationCountResponse
|
||||
*/
|
||||
@ -449,16 +466,6 @@ export const zTextToSpeechPayload = z.object({
|
||||
voice: z.string().nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
* AudioBinaryResponse
|
||||
*/
|
||||
export const zAudioBinaryResponse = z.custom<Blob | File>()
|
||||
|
||||
/**
|
||||
* TextToSpeechVoiceListResponse
|
||||
*/
|
||||
export const zTextToSpeechVoiceListResponse = z.array(z.record(z.string(), z.unknown()))
|
||||
|
||||
/**
|
||||
* AppTraceResponse
|
||||
*/
|
||||
@ -1251,9 +1258,9 @@ export const zAgentSkillUploadResponse = z.object({
|
||||
})
|
||||
|
||||
/**
|
||||
* AnnotationEmbeddingModelResponse
|
||||
* AnnotationSettingEmbeddingModelResponse
|
||||
*/
|
||||
export const zAnnotationEmbeddingModelResponse = z.object({
|
||||
export const zAnnotationSettingEmbeddingModelResponse = z.object({
|
||||
embedding_model_name: z.string().nullish(),
|
||||
embedding_provider_name: z.string().nullish(),
|
||||
})
|
||||
@ -1262,7 +1269,7 @@ export const zAnnotationEmbeddingModelResponse = z.object({
|
||||
* AnnotationSettingResponse
|
||||
*/
|
||||
export const zAnnotationSettingResponse = z.object({
|
||||
embedding_model: zAnnotationEmbeddingModelResponse.nullish(),
|
||||
embedding_model: zAnnotationSettingEmbeddingModelResponse.nullish(),
|
||||
enabled: z.boolean(),
|
||||
id: z.string().nullish(),
|
||||
score_threshold: z.number().nullish(),
|
||||
@ -1456,10 +1463,13 @@ export const zDailyMessageStatisticResponse = z.object({
|
||||
* DailyTokenCostStatisticItem
|
||||
*/
|
||||
export const zDailyTokenCostStatisticItem = z.object({
|
||||
currency: z.string(),
|
||||
currency: z.string().nullish(),
|
||||
date: z.string(),
|
||||
token_count: z.int(),
|
||||
total_price: z.union([z.string(), z.number()]),
|
||||
token_count: z.int().nullish(),
|
||||
total_price: z
|
||||
.string()
|
||||
.regex(/^(?![-+.]*$)[+-]?\d*(?:\.\d*)?$/)
|
||||
.nullish(),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -1499,6 +1509,21 @@ export const zUserSatisfactionRateStatisticResponse = z.object({
|
||||
data: z.array(zUserSatisfactionRateStatisticItem),
|
||||
})
|
||||
|
||||
/**
|
||||
* TextToSpeechVoiceResponse
|
||||
*/
|
||||
export const zTextToSpeechVoiceResponse = z.object({
|
||||
name: z.string(),
|
||||
value: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* TextToSpeechVoiceListResponse
|
||||
*
|
||||
* Available voices
|
||||
*/
|
||||
export const zTextToSpeechVoiceListResponse = z.array(zTextToSpeechVoiceResponse)
|
||||
|
||||
/**
|
||||
* SimpleAccount
|
||||
*/
|
||||
@ -1574,6 +1599,7 @@ export const zMessageDetail = z.object({
|
||||
agent_thoughts: z.array(zAgentThought),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
|
||||
answer: z.string(),
|
||||
answer_tokens: z.int(),
|
||||
conversation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
@ -1586,12 +1612,11 @@ export const zMessageDetail = z.object({
|
||||
inputs: z.record(z.string(), zJsonValue),
|
||||
message: zJsonValue,
|
||||
message_files: z.array(zMessageFile),
|
||||
message_metadata_dict: zJsonValue,
|
||||
message_tokens: z.int(),
|
||||
metadata: zJsonValue,
|
||||
parent_message_id: z.string().nullish(),
|
||||
provider_response_latency: z.number(),
|
||||
query: z.string(),
|
||||
re_sign_file_url_answer: z.string(),
|
||||
status: z.string(),
|
||||
workflow_run_id: z.string().nullish(),
|
||||
})
|
||||
@ -2375,11 +2400,11 @@ export const zConversationDetail = z.object({
|
||||
*/
|
||||
export const zConversationMessageDetail = z.object({
|
||||
created_at: z.int().nullish(),
|
||||
first_message: zMessageDetail.nullish(),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
message: zMessageDetail.nullish(),
|
||||
model_config: zModelConfig.nullish(),
|
||||
status: z.string(),
|
||||
})
|
||||
@ -2525,7 +2550,7 @@ export const zSkillToolInferenceResult = z.object({
|
||||
* SimpleModelConfig
|
||||
*/
|
||||
export const zSimpleModelConfig = z.object({
|
||||
model_dict: zJsonValue.nullish(),
|
||||
model: zJsonValue.nullish(),
|
||||
pre_prompt: z.string().nullish(),
|
||||
})
|
||||
|
||||
@ -2558,7 +2583,7 @@ export const zConversationWithSummary = z.object({
|
||||
read_at: z.int().nullish(),
|
||||
status: z.string(),
|
||||
status_count: zStatusCount.nullish(),
|
||||
summary_or_query: z.string(),
|
||||
summary: z.string(),
|
||||
updated_at: z.int().nullish(),
|
||||
user_feedback_stats: zFeedbackStat.nullish(),
|
||||
})
|
||||
@ -2567,10 +2592,10 @@ export const zConversationWithSummary = z.object({
|
||||
* ConversationWithSummaryPagination
|
||||
*/
|
||||
export const zConversationWithSummaryPagination = z.object({
|
||||
has_next: z.boolean(),
|
||||
items: z.array(zConversationWithSummary),
|
||||
data: z.array(zConversationWithSummary),
|
||||
has_more: z.boolean(),
|
||||
limit: z.int(),
|
||||
page: z.int(),
|
||||
per_page: z.int(),
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
@ -2591,13 +2616,13 @@ export const zConversation = z.object({
|
||||
admin_feedback_stats: zFeedbackStat.nullish(),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
created_at: z.int().nullish(),
|
||||
first_message: zSimpleMessageDetail.nullish(),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_account_name: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_end_user_session_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
message: zSimpleMessageDetail.nullish(),
|
||||
model_config: zSimpleModelConfig.nullish(),
|
||||
read_at: z.int().nullish(),
|
||||
status: z.string(),
|
||||
@ -2609,10 +2634,10 @@ export const zConversation = z.object({
|
||||
* ConversationPagination
|
||||
*/
|
||||
export const zConversationPagination = z.object({
|
||||
has_next: z.boolean(),
|
||||
items: z.array(zConversation),
|
||||
data: z.array(zConversation),
|
||||
has_more: z.boolean(),
|
||||
limit: z.int(),
|
||||
page: z.int(),
|
||||
per_page: z.int(),
|
||||
total: z.int(),
|
||||
})
|
||||
|
||||
@ -3847,27 +3872,27 @@ export const zHumanInputContent = z.object({
|
||||
* MessageDetailResponse
|
||||
*/
|
||||
export const zMessageDetailResponse = z.object({
|
||||
agent_thoughts: z.array(zAgentThought).optional(),
|
||||
agent_thoughts: z.array(zAgentThought),
|
||||
annotation: zConversationAnnotation.nullish(),
|
||||
annotation_hit_history: zConversationAnnotationHitHistory.nullish(),
|
||||
answer: z.string(),
|
||||
answer_tokens: z.int().nullish(),
|
||||
answer_tokens: z.int(),
|
||||
conversation_id: z.string(),
|
||||
created_at: z.int().nullish(),
|
||||
error: z.string().nullish(),
|
||||
extra_contents: z.array(zHumanInputContent).optional(),
|
||||
feedbacks: z.array(zFeedback).optional(),
|
||||
feedbacks: z.array(zFeedback),
|
||||
from_account_id: z.string().nullish(),
|
||||
from_end_user_id: z.string().nullish(),
|
||||
from_source: z.string(),
|
||||
id: z.string(),
|
||||
inputs: z.record(z.string(), zJsonValue),
|
||||
message: zJsonValue.nullish(),
|
||||
message_files: z.array(zMessageFile).optional(),
|
||||
message_tokens: z.int().nullish(),
|
||||
metadata: zJsonValue.nullish(),
|
||||
message: zJsonValue,
|
||||
message_files: z.array(zMessageFile),
|
||||
message_tokens: z.int(),
|
||||
metadata: zJsonValue,
|
||||
parent_message_id: z.string().nullish(),
|
||||
provider_response_latency: z.number().nullish(),
|
||||
provider_response_latency: z.number(),
|
||||
query: z.string(),
|
||||
status: z.string(),
|
||||
workflow_run_id: z.string().nullish(),
|
||||
@ -4886,7 +4911,7 @@ export const zGetAppsByAppIdAnnotationReplyByActionStatusByJobIdPath = z.object(
|
||||
* Job status retrieved successfully
|
||||
*/
|
||||
export const zGetAppsByAppIdAnnotationReplyByActionStatusByJobIdResponse
|
||||
= zAnnotationJobStatusResponse
|
||||
= zAnnotationJobStatusDetailResponse
|
||||
|
||||
export const zGetAppsByAppIdAnnotationSettingPath = z.object({
|
||||
app_id: z.uuid(),
|
||||
@ -4953,7 +4978,7 @@ export const zPostAppsByAppIdAnnotationsBatchImportPath = z.object({
|
||||
/**
|
||||
* Batch import started successfully
|
||||
*/
|
||||
export const zPostAppsByAppIdAnnotationsBatchImportResponse = zAnnotationJobStatusResponse
|
||||
export const zPostAppsByAppIdAnnotationsBatchImportResponse = zAnnotationBatchImportResponse
|
||||
|
||||
export const zGetAppsByAppIdAnnotationsBatchImportStatusByJobIdPath = z.object({
|
||||
app_id: z.uuid(),
|
||||
@ -4964,7 +4989,7 @@ export const zGetAppsByAppIdAnnotationsBatchImportStatusByJobIdPath = z.object({
|
||||
* Job status retrieved successfully
|
||||
*/
|
||||
export const zGetAppsByAppIdAnnotationsBatchImportStatusByJobIdResponse
|
||||
= zAnnotationJobStatusResponse
|
||||
= zAnnotationJobStatusDetailResponse
|
||||
|
||||
export const zGetAppsByAppIdAnnotationsCountPath = z.object({
|
||||
app_id: z.uuid(),
|
||||
@ -5165,7 +5190,7 @@ export const zPostAppsByAppIdCompletionMessagesPath = z.object({
|
||||
/**
|
||||
* Completion generated successfully
|
||||
*/
|
||||
export const zPostAppsByAppIdCompletionMessagesResponse = zGeneratedAppResponse
|
||||
export const zPostAppsByAppIdCompletionMessagesResponse = z.record(z.string(), z.unknown())
|
||||
|
||||
export const zPostAppsByAppIdCompletionMessagesByTaskIdStopPath = z.object({
|
||||
app_id: z.uuid(),
|
||||
@ -5509,7 +5534,7 @@ export const zPostAppsByAppIdTextToAudioPath = z.object({
|
||||
/**
|
||||
* Text to speech conversion successful
|
||||
*/
|
||||
export const zPostAppsByAppIdTextToAudioResponse = zAudioBinaryResponse
|
||||
export const zPostAppsByAppIdTextToAudioResponse = z.record(z.string(), z.unknown())
|
||||
|
||||
export const zGetAppsByAppIdTextToAudioVoicesPath = z.object({
|
||||
app_id: z.uuid(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user