2026-05-08 08:56:52 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import Literal
|
|
|
|
|
|
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsCompanyForm(BaseModel):
|
|
|
|
|
companyName: str = Field(min_length=1, max_length=120)
|
|
|
|
|
displayName: str = Field(min_length=1, max_length=120)
|
|
|
|
|
companyCode: str = Field(default="", max_length=64)
|
|
|
|
|
recordNumber: str = Field(default="", max_length=120)
|
|
|
|
|
copyright: str = Field(default="", max_length=255)
|
|
|
|
|
|
|
|
|
|
@field_validator("companyName", "displayName", "companyCode", "recordNumber", "copyright", mode="before")
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsAdminForm(BaseModel):
|
|
|
|
|
adminAccount: str = Field(min_length=1, max_length=120)
|
|
|
|
|
adminEmail: str = Field(min_length=1, max_length=255)
|
|
|
|
|
newPassword: str = Field(default="", max_length=128)
|
|
|
|
|
confirmPassword: str = Field(default="", max_length=128)
|
|
|
|
|
sessionTimeout: int = Field(default=30, ge=5, le=240)
|
|
|
|
|
noticeEmail: str = Field(default="", max_length=255)
|
|
|
|
|
mfaEnabled: bool = True
|
|
|
|
|
strongPassword: bool = True
|
|
|
|
|
loginAlertEnabled: bool = True
|
|
|
|
|
adminPasswordConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
@field_validator("adminAccount", "adminEmail", "newPassword", "confirmPassword", "noticeEmail", mode="before")
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsLlmForm(BaseModel):
|
|
|
|
|
mainProvider: str = Field(min_length=1, max_length=64)
|
|
|
|
|
mainModel: str = Field(min_length=1, max_length=255)
|
|
|
|
|
mainEndpoint: str = Field(min_length=1, max_length=512)
|
|
|
|
|
mainApiKey: str = Field(default="", max_length=1024)
|
|
|
|
|
mainApiKeyConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
backupProvider: str = Field(min_length=1, max_length=64)
|
|
|
|
|
backupModel: str = Field(min_length=1, max_length=255)
|
|
|
|
|
backupEndpoint: str = Field(min_length=1, max_length=512)
|
|
|
|
|
backupApiKey: str = Field(default="", max_length=1024)
|
|
|
|
|
backupApiKeyConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
vlmProvider: str = Field(min_length=1, max_length=64)
|
|
|
|
|
vlmModel: str = Field(min_length=1, max_length=255)
|
|
|
|
|
vlmEndpoint: str = Field(min_length=1, max_length=512)
|
|
|
|
|
vlmApiKey: str = Field(default="", max_length=1024)
|
|
|
|
|
vlmApiKeyConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
embeddingProvider: str = Field(min_length=1, max_length=64)
|
|
|
|
|
embeddingModel: str = Field(min_length=1, max_length=255)
|
|
|
|
|
embeddingEndpoint: str = Field(min_length=1, max_length=512)
|
|
|
|
|
embeddingApiKey: str = Field(default="", max_length=1024)
|
|
|
|
|
embeddingApiKeyConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
@field_validator(
|
|
|
|
|
"mainProvider",
|
|
|
|
|
"mainModel",
|
|
|
|
|
"mainEndpoint",
|
|
|
|
|
"mainApiKey",
|
|
|
|
|
"backupProvider",
|
|
|
|
|
"backupModel",
|
|
|
|
|
"backupEndpoint",
|
|
|
|
|
"backupApiKey",
|
|
|
|
|
"vlmProvider",
|
|
|
|
|
"vlmModel",
|
|
|
|
|
"vlmEndpoint",
|
|
|
|
|
"vlmApiKey",
|
|
|
|
|
"embeddingProvider",
|
|
|
|
|
"embeddingModel",
|
|
|
|
|
"embeddingEndpoint",
|
|
|
|
|
"embeddingApiKey",
|
|
|
|
|
mode="before",
|
|
|
|
|
)
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsLogForm(BaseModel):
|
|
|
|
|
level: str = Field(min_length=1, max_length=16)
|
|
|
|
|
retentionDays: int = Field(default=180, ge=1, le=3650)
|
|
|
|
|
archiveCycle: str = Field(default="weekly", max_length=32)
|
|
|
|
|
logPath: str = Field(min_length=1, max_length=255)
|
|
|
|
|
alertEmail: str = Field(default="", max_length=255)
|
|
|
|
|
operationAudit: bool = True
|
|
|
|
|
loginAudit: bool = True
|
|
|
|
|
maskSensitive: bool = True
|
|
|
|
|
|
|
|
|
|
@field_validator("level", "archiveCycle", "logPath", "alertEmail", mode="before")
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
2026-05-09 08:02:01 +00:00
|
|
|
class SettingsRenderForm(BaseModel):
|
|
|
|
|
enabled: bool = False
|
|
|
|
|
publicUrl: str = Field(default="", max_length=512)
|
|
|
|
|
jwtSecret: str = Field(default="", max_length=1024)
|
|
|
|
|
jwtSecretConfigured: bool = False
|
|
|
|
|
|
|
|
|
|
@field_validator("publicUrl", "jwtSecret", mode="before")
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
2026-05-08 08:56:52 +08:00
|
|
|
class SettingsMailForm(BaseModel):
|
|
|
|
|
smtpHost: str = Field(min_length=1, max_length=255)
|
|
|
|
|
port: int = Field(default=465, ge=1, le=65535)
|
|
|
|
|
encryption: str = Field(default="SSL/TLS", max_length=32)
|
|
|
|
|
senderName: str = Field(default="", max_length=120)
|
|
|
|
|
senderAddress: str = Field(default="", max_length=255)
|
|
|
|
|
username: str = Field(default="", max_length=255)
|
|
|
|
|
password: str = Field(default="", max_length=1024)
|
|
|
|
|
passwordConfigured: bool = False
|
|
|
|
|
alertEnabled: bool = True
|
|
|
|
|
digestEnabled: bool = False
|
|
|
|
|
digestTime: str = Field(default="09:00", max_length=16)
|
|
|
|
|
defaultReceiver: str = Field(default="", max_length=255)
|
|
|
|
|
|
|
|
|
|
@field_validator(
|
|
|
|
|
"smtpHost",
|
|
|
|
|
"encryption",
|
|
|
|
|
"senderName",
|
|
|
|
|
"senderAddress",
|
|
|
|
|
"username",
|
|
|
|
|
"password",
|
|
|
|
|
"digestTime",
|
|
|
|
|
"defaultReceiver",
|
|
|
|
|
mode="before",
|
|
|
|
|
)
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsRead(BaseModel):
|
|
|
|
|
companyForm: SettingsCompanyForm
|
|
|
|
|
adminForm: SettingsAdminForm
|
|
|
|
|
llmForm: SettingsLlmForm
|
2026-05-09 08:02:01 +00:00
|
|
|
renderForm: SettingsRenderForm
|
2026-05-08 08:56:52 +08:00
|
|
|
logForm: SettingsLogForm
|
|
|
|
|
mailForm: SettingsMailForm
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsWrite(BaseModel):
|
|
|
|
|
companyForm: SettingsCompanyForm
|
|
|
|
|
adminForm: SettingsAdminForm
|
|
|
|
|
llmForm: SettingsLlmForm
|
2026-05-09 08:02:01 +00:00
|
|
|
renderForm: SettingsRenderForm
|
2026-05-08 08:56:52 +08:00
|
|
|
logForm: SettingsLogForm
|
|
|
|
|
mailForm: SettingsMailForm
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ModelConnectivityTestRequest(BaseModel):
|
|
|
|
|
provider: str = Field(min_length=1, max_length=64)
|
|
|
|
|
endpoint: str = Field(min_length=1, max_length=512)
|
|
|
|
|
model: str = Field(min_length=1, max_length=255)
|
|
|
|
|
api_key: str | None = Field(default=None, max_length=1024)
|
|
|
|
|
capability: Literal["chat", "embedding"] = "chat"
|
|
|
|
|
slot: Literal["main", "backup", "vlm", "embedding"] | None = None
|
|
|
|
|
|
|
|
|
|
@field_validator("provider", "endpoint", "model", "api_key", mode="before")
|
|
|
|
|
@classmethod
|
|
|
|
|
def strip_model_string(cls, value: str | None) -> str | None:
|
|
|
|
|
if value is None:
|
|
|
|
|
return None
|
|
|
|
|
return value.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ModelConnectivityTestRead(BaseModel):
|
|
|
|
|
ok: bool
|
|
|
|
|
provider: str
|
|
|
|
|
model: str
|
|
|
|
|
endpoint: str
|
|
|
|
|
capability: str
|
|
|
|
|
detail: str
|
|
|
|
|
status_code: int | None = None
|
|
|
|
|
checked_at: datetime
|
2026-05-09 09:14:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RuntimeModelConfigRead(BaseModel):
|
|
|
|
|
slot: Literal["main", "backup", "vlm", "embedding"]
|
|
|
|
|
provider: str
|
|
|
|
|
model: str
|
|
|
|
|
endpoint: str
|
|
|
|
|
apiKey: str
|
|
|
|
|
capability: Literal["chat", "embedding"]
|