feat: add FastAPI manager API compatibility baseline

This commit is contained in:
Tyke Chen
2026-07-20 17:00:13 +08:00
parent 7c58fa37b2
commit 804ddb51f2
140 changed files with 47169 additions and 1 deletions
@@ -0,0 +1 @@
"""Pydantic request and response schemas."""
@@ -0,0 +1,238 @@
from __future__ import annotations
import json
from datetime import datetime
from typing import Any
from pydantic import Field, field_validator
from pydantic_core import PydanticCustomError
from app.schemas.common import JavaModel
class AgentCreate(JavaModel):
agent_name: str
@field_validator("agent_name", mode="before")
@classmethod
def require_non_blank_name(cls, value: Any) -> Any:
if value is None or isinstance(value, str) and not value.strip():
raise PydanticCustomError("java_not_blank", "智能体名称不能为空")
return value
class AgentMemory(JavaModel):
summary_memory: str | None = None
class ContextProvider(JavaModel):
url: str | None = None
headers: dict[str, Any] | None = None
class FunctionInfo(JavaModel):
plugin_id: str | None = None
param_info: dict[str, Any] = Field(default_factory=dict)
@field_validator("param_info", mode="before")
@classmethod
def normalize_param_info(cls, value: Any) -> dict[str, Any]:
if value is None or value == "":
return {}
if isinstance(value, str):
parsed = json.loads(value)
if not isinstance(parsed, dict):
raise ValueError("paramInfo must be a JSON object")
return {str(key): item for key, item in parsed.items()}
if isinstance(value, dict):
return {str(key): item for key, item in value.items() if key is not None}
parsed = json.loads(json.dumps(value))
if not isinstance(parsed, dict):
raise ValueError("paramInfo must be an object")
return {str(key): item for key, item in parsed.items()}
class AgentUpdate(JavaModel):
agent_code: str | None = None
agent_name: str | None = None
asr_model_id: str | None = None
vad_model_id: str | None = None
llm_model_id: str | None = None
slm_model_id: str | None = None
vllm_model_id: str | None = None
tts_model_id: str | None = None
tts_voice_id: str | None = None
tts_language: str | None = None
tts_volume: int | None = None
tts_rate: int | None = None
tts_pitch: int | None = None
mem_model_id: str | None = None
intent_model_id: str | None = None
functions: list[FunctionInfo] | None = None
system_prompt: str | None = None
summary_memory: str | None = None
chat_history_conf: int | None = None
lang_code: str | None = None
language: str | None = None
sort: int | None = None
context_providers: list[ContextProvider] | None = None
correct_word_file_ids: list[str] | None = None
tag_names: list[str] | None = None
tag_ids: list[str] | None = None
class AgentChatHistoryReport(JavaModel):
mac_address: str
session_id: str
chat_type: int
content: str
audio_base64: str | None = None
report_time: int | None = None
@field_validator("mac_address", "session_id", "content", mode="before")
@classmethod
def require_non_blank(cls, value: Any) -> Any:
if value is None or isinstance(value, str) and not value.strip():
raise PydanticCustomError("java_not_blank", "不能为空")
return value
@field_validator("chat_type", mode="before")
@classmethod
def require_chat_type(cls, value: Any) -> Any:
if value is None:
raise PydanticCustomError("java_not_null", "不能为空")
return value
class AgentSnapshotPage(JavaModel):
page: int | None = 1
limit: int | None = 10
max_version_no: int | None = None
def page_or_default(self) -> int:
return self.page if self.page is not None and self.page >= 1 else 1
def limit_or_default(self) -> int:
return self.limit if self.limit is not None and self.limit >= 1 else 10
class AgentSnapshotRestore(JavaModel):
current_state_token: str
@field_validator("current_state_token", mode="before")
@classmethod
def require_non_blank_token(cls, value: Any) -> Any:
if value is None or isinstance(value, str) and not value.strip():
raise PydanticCustomError("java_not_blank", "不能为空")
return value
class AgentSnapshotTag(JavaModel):
id: str | None = None
tag_name: str | None = None
sort: int | None = None
class AgentSnapshotData(JavaModel):
agent_code: str | None = None
agent_name: str | None = None
asr_model_id: str | None = None
vad_model_id: str | None = None
llm_model_id: str | None = None
slm_model_id: str | None = None
vllm_model_id: str | None = None
tts_model_id: str | None = None
tts_voice_id: str | None = None
tts_language: str | None = None
tts_volume: int | None = None
tts_rate: int | None = None
tts_pitch: int | None = None
mem_model_id: str | None = None
intent_model_id: str | None = None
chat_history_conf: int | None = None
system_prompt: str | None = None
summary_memory: str | None = None
lang_code: str | None = None
language: str | None = None
sort: int | None = None
functions: list[FunctionInfo] | None = None
context_providers: list[ContextProvider] | None = None
correct_word_file_ids: list[str] | None = None
tag_names: list[str] | None = None
tags: list[AgentSnapshotTag] | None = None
class AgentTemplate(JavaModel):
id: str | None = None
agent_code: str | None = None
agent_name: str | None = None
asr_model_id: str | None = None
vad_model_id: str | None = None
llm_model_id: str | None = None
vllm_model_id: str | None = None
tts_model_id: str | None = None
tts_voice_id: str | None = None
tts_language: str | None = None
tts_volume: int | None = None
tts_rate: int | None = None
tts_pitch: int | None = None
mem_model_id: str | None = None
intent_model_id: str | None = None
chat_history_conf: int | None = None
system_prompt: str | None = None
summary_memory: str | None = None
lang_code: str | None = None
language: str | None = None
sort: int | None = None
creator: int | None = None
created_at: datetime | None = None
updater: int | None = None
updated_at: datetime | None = None
class AgentVoicePrintSave(JavaModel):
agent_id: str | None = None
audio_id: str | None = None
source_name: str | None = None
introduce: str | None = None
class AgentVoicePrintUpdate(JavaModel):
id: str | None = None
audio_id: str | None = None
source_name: str | None = None
introduce: str | None = None
class AgentTagAssignment(JavaModel):
tag_ids: list[str] | None = None
tag_names: list[str] | None = None
SNAPSHOT_FIELD_ORDER = [
"agentCode",
"agentName",
"asrModelId",
"vadModelId",
"llmModelId",
"slmModelId",
"vllmModelId",
"ttsModelId",
"ttsVoiceId",
"ttsLanguage",
"ttsVolume",
"ttsRate",
"ttsPitch",
"memModelId",
"intentModelId",
"chatHistoryConf",
"systemPrompt",
"summaryMemory",
"langCode",
"language",
"sort",
"functions",
"contextProviders",
"correctWordFileIds",
"tagNames",
]
@@ -0,0 +1,57 @@
from __future__ import annotations
from collections.abc import Callable
from typing import Any, Generic, TypeVar
from pydantic import BaseModel, ConfigDict, Field
T = TypeVar("T")
def to_camel(value: str) -> str:
head, *tail = value.split("_")
return head + "".join(part[:1].upper() + part[1:] for part in tail)
class JavaModel(BaseModel):
model_config = ConfigDict(
alias_generator=to_camel,
populate_by_name=True,
extra="ignore",
str_strip_whitespace=False,
serialize_by_alias=True,
)
class PageData(JavaModel, Generic[T]):
total: int
list: list[T]
class PageQuery(JavaModel):
page: int = Field(default=1, ge=1)
limit: int = Field(default=10, ge=1)
order_field: str | list[str] | None = None
order: str | None = None
class DeleteIds(JavaModel):
ids: list[str]
def page_payload(rows: list[Any], total: int) -> dict[str, Any]:
return {"total": int(total), "list": rows}
def safe_order_by(
requested: str | list[str] | None,
*,
allowed: set[str],
default: str,
transform: Callable[[str], str] | None = None,
) -> list[str]:
fields = [requested] if isinstance(requested, str) else list(requested or [])
selected = [field for field in fields if field in allowed]
if not selected:
selected = [default]
return [transform(field) if transform else field for field in selected]
@@ -0,0 +1,25 @@
from __future__ import annotations
from pydantic import field_validator
from app.schemas.common import JavaModel
def _not_blank(value: str) -> str:
if not value or not value.strip():
raise ValueError("must not be blank")
return value
class AgentModelsRequest(JavaModel):
mac_address: str
client_id: str
selected_module: dict[str, str]
_validate_required = field_validator("mac_address", "client_id")(_not_blank)
class CorrectWordsRequest(JavaModel):
mac_address: str
_validate_required = field_validator("mac_address")(_not_blank)
@@ -0,0 +1,9 @@
from __future__ import annotations
from app.schemas.common import JavaModel
class CorrectWordFileBody(JavaModel):
file_name: str | None = None
content: list[str] | None = None
file_size: int | None = None
@@ -0,0 +1,145 @@
from __future__ import annotations
from typing import Any
from pydantic import AliasChoices, Field
from app.schemas.common import JavaModel
class DeviceRegisterRequest(JavaModel):
mac_address: str | None = None
class DeviceUnbindRequest(JavaModel):
device_id: str | None = None
class DeviceUpdateRequest(JavaModel):
auto_update: int | None = None
alias: str | None = None
class DeviceManualAddRequest(JavaModel):
agent_id: str | None = None
board: str | None = None
app_version: str | None = None
mac_address: str | None = None
class DeviceToolCallRequest(JavaModel):
name: str | None = None
arguments: dict[str, Any] | None = None
class DeviceAddressBookAliasRequest(JavaModel):
mac_address: str | None = None
target_mac: str | None = None
alias: str | None = None
class DeviceAddressBookPermissionRequest(JavaModel):
mac_address: str | None = None
target_mac: str | None = None
has_permission: bool | None = None
class ChipInfo(JavaModel):
model: int | None = None
cores: int | None = None
revision: int | None = None
features: int | None = None
class ApplicationInfo(JavaModel):
name: str | None = None
version: str | None = None
compile_time: str | None = Field(
default=None,
validation_alias=AliasChoices("compile_time", "compileTime"),
serialization_alias="compile_time",
)
idf_version: str | None = Field(
default=None,
validation_alias=AliasChoices("idf_version", "idfVersion"),
serialization_alias="idf_version",
)
elf_sha256: str | None = Field(
default=None,
validation_alias=AliasChoices("elf_sha256", "elfSha256"),
serialization_alias="elf_sha256",
)
class PartitionInfo(JavaModel):
label: str | None = None
type: int | None = None
subtype: int | None = None
address: int | None = None
size: int | None = None
class OtaPartitionInfo(JavaModel):
label: str | None = None
class BoardInfo(JavaModel):
type: str | None = None
ssid: str | None = None
rssi: int | None = None
channel: int | None = None
ip: str | None = None
mac: str | None = None
class DeviceReportRequest(JavaModel):
version: int | None = None
flash_size: int | None = Field(
default=None,
validation_alias=AliasChoices("flash_size", "flashSize"),
serialization_alias="flash_size",
)
minimum_free_heap_size: int | None = Field(
default=None,
validation_alias=AliasChoices("minimum_free_heap_size", "minimumFreeHeapSize"),
serialization_alias="minimum_free_heap_size",
)
mac_address: str | None = Field(
default=None,
validation_alias=AliasChoices("mac_address", "macAddress"),
serialization_alias="mac_address",
)
uuid: str | None = None
chip_model_name: str | None = Field(
default=None,
validation_alias=AliasChoices("chip_model_name", "chipModelName"),
serialization_alias="chip_model_name",
)
chip_info: ChipInfo | None = Field(
default=None,
validation_alias=AliasChoices("chip_info", "chipInfo"),
serialization_alias="chip_info",
)
application: ApplicationInfo | None = None
partition_table: list[PartitionInfo] | None = Field(
default=None,
validation_alias=AliasChoices("partition_table", "partitionTable"),
serialization_alias="partition_table",
)
ota: OtaPartitionInfo | None = None
board: BoardInfo | None = None
class OtaRecord(JavaModel):
id: str | None = None
firmware_name: str | None = None
type: str | None = None
version: str | None = None
size: int | None = None
remark: str | None = None
firmware_path: str | None = None
sort: int | None = None
updater: int | None = None
update_date: str | None = None
creator: int | None = None
create_date: str | None = None
@@ -0,0 +1,53 @@
from __future__ import annotations
from datetime import datetime
from typing import Any
from pydantic import AliasChoices, Field
from app.schemas.common import JavaModel
class KnowledgeBaseBody(JavaModel):
id: str | None = None
dataset_id: str | None = None
rag_model_id: str | None = None
name: str | None = None
avatar: str | None = None
description: str | None = None
embedding_model: str | None = None
permission: str | None = None
chunk_method: str | None = None
parser_config: str | None = None
chunk_count: int | None = None
token_num: int | None = None
status: int | None = None
creator: int | None = None
created_at: datetime | None = None
updater: int | None = None
updated_at: datetime | None = None
document_count: int | None = None
error_message: str | None = None
class DocumentBatchBody(JavaModel):
ids: list[str] | None = Field(
default=None,
validation_alias=AliasChoices("ids", "document_ids"),
)
class RetrievalBody(JavaModel):
dataset_ids: list[str] | None = None
document_ids: list[str] | None = None
question: str | None = None
page: int | None = None
page_size: int | None = None
similarity_threshold: float | None = None
vector_similarity_weight: float | None = None
top_k: int | None = None
rerank_id: str | None = None
highlight: bool | None = None
keyword: bool | None = None
cross_languages: list[str] | None = None
metadata_condition: dict[str, Any] | None = None
@@ -0,0 +1,25 @@
from __future__ import annotations
from typing import Any
from app.schemas.common import JavaModel
class ModelConfigBody(JavaModel):
id: str | None = None
model_code: str | None = None
model_name: str | None = None
is_default: int | None = None
is_enabled: int | None = None
config_json: dict[str, Any] | None = None
doc_link: str | None = None
remark: str | None = None
sort: int | None = None
class ModelProviderBody(JavaModel):
id: str | None = None
model_type: str | None = None
provider_code: str | None = None
name: str | None = None
fields: str | None = None
sort: int | None = None
@@ -0,0 +1,59 @@
from __future__ import annotations
from typing import Any
from app.schemas.common import JavaModel
class LoginRequest(JavaModel):
# LoginController does not use @Valid; null/blank values reach its service logic.
username: str | None = None
password: str | None = None
mobile_captcha: str | None = None
captcha_id: str | None = None
class SmsVerificationRequest(JavaModel):
# smsVerification likewise omits @Valid in the Java controller.
phone: str | None = None
captcha: str | None = None
captcha_id: str | None = None
class PasswordChangeRequest(JavaModel):
password: str | None = None
new_password: str | None = None
class RetrievePasswordRequest(JavaModel):
phone: str | None = None
code: str | None = None
password: str | None = None
captcha_id: str | None = None
class TokenData(JavaModel):
token: str
expire: int
client_hash: str | None
class UserDetailData(JavaModel):
id: int
username: str
super_admin: int
token: str
status: int
class PublicConfigData(JavaModel):
enable_mobile_register: bool
version: str
year: str
allow_user_register: bool
mobile_area_list: list[dict[str, Any]]
beian_icp_num: str | None
beian_ga_num: str | None
name: str | None
sm2_public_key: str
system_web_menu: Any | None = None
@@ -0,0 +1,45 @@
from __future__ import annotations
from pydantic import field_validator
from app.schemas.common import JavaModel
def _not_blank(value: str) -> str:
if not value or not value.strip():
raise ValueError("must not be blank")
return value
class SysParamPayload(JavaModel):
id: int | None = None
param_code: str | None = None
param_value: str | None = None
value_type: str | None = None
remark: str | None = None
class DictTypePayload(JavaModel):
# Controller calls ValidatorUtils without the DTO's custom groups, so these constraints don't execute in Java.
id: int | None = None
dict_type: str | None = None
dict_name: str | None = None
remark: str | None = None
sort: int | None = None
class DictDataPayload(JavaModel):
# See DictTypePayload: Add/Update/DefaultGroup annotations are skipped by the Java controller.
id: int | None = None
dict_type_id: int | None = None
dict_label: str | None = None
dict_value: str | None = None
remark: str | None = None
sort: int | None = None
class EmitServerActionRequest(JavaModel):
target_ws: str
action: str | None
_validate_target = field_validator("target_ws")(_not_blank)
@@ -0,0 +1,15 @@
from __future__ import annotations
from app.schemas.common import JavaModel
class TimbreBody(JavaModel):
languages: str | None = None
name: str | None = None
remark: str | None = None
reference_audio: str | None = None
reference_text: str | None = None
sort: int | None = 0
tts_model_id: str | None = None
tts_voice: str | None = None
voice_demo: str | None = None
@@ -0,0 +1,19 @@
from __future__ import annotations
from app.schemas.common import JavaModel
class VoiceResourceCreateRequest(JavaModel):
model_id: str | None = None
voice_ids: list[str] | None = None
user_id: int | None = None
languages: str | None = None
class VoiceCloneRenameRequest(JavaModel):
id: str | None = None
name: str | None = None
class VoiceCloneTrainRequest(JavaModel):
clone_id: str | None = None