prepare fastapi root layout for server deployment

This commit is contained in:
刘金宝
2026-06-04 10:55:23 +08:00
parent eb43573a44
commit b46e43aadc
103 changed files with 347 additions and 197 deletions
+1
View File
@@ -0,0 +1 @@
"""Pydantic 入参和出参模型。"""
+19
View File
@@ -0,0 +1,19 @@
from pydantic import BaseModel
class AgentHelloUser(BaseModel):
"""Hello 用户信息:返回宿主系统传入的上下文。"""
user_id: str
tenant_id: str | None = None
role: str | None = None
source: str | None = None
username: str | None = None
display_name: str | None = None
class AgentHelloResponse(BaseModel):
"""Hello 响应:返回用户上下文和 Demo 能力开关。"""
user: AgentHelloUser
features: dict
+43
View File
@@ -0,0 +1,43 @@
from typing import Any
from pydantic import BaseModel
class AuthMeResponse(BaseModel):
"""认证用户响应:返回医疗问诊 Agent 标准化后的当前用户信息。"""
user_id: str
source: str
username: str | None = None
display_name: str | None = None
tenant_id: str | None = None
role: str | None = None
phone: str | None = None
avatar: str | None = None
gender: int | None = None
institution: int | None = None
institution_id: int | None = None
institution_name: str | None = None
department: int | None = None
department_id: int | None = None
department_name: str | None = None
title_name: str | None = None
major: str | None = None
training_stage: str | None = None
learning_target: str | None = None
competency_profile: dict[str, Any] | None = None
weak_dimensions: list[Any] | None = None
strong_dimensions: list[Any] | None = None
ai_preference: dict[str, Any] | None = None
total_training_count: int | None = None
total_case_count: int | None = None
current_level: str | None = None
status: int | None = None
last_login: str | None = None
last_login_time: str | None = None
is_superuser: bool | None = None
is_staff: bool | None = None
is_active: bool | None = None
date_joined: str | None = None
created_at: str | None = None
updated_at: str | None = None
+76
View File
@@ -0,0 +1,76 @@
from pydantic import BaseModel, ConfigDict
class CaseListItem(BaseModel):
"""病例列表项:不暴露标准答案和隐藏信息。"""
id: int
case_code: str
department_id: int
title: str
difficulty: str
chief_complaint: str | None = None
supported_training_type: str
supported_mode: str
has_teaching_video: bool
has_knowledge_points: bool
has_quiz: bool
model_config = ConfigDict(from_attributes=True)
class CaseListResponse(BaseModel):
"""病例列表响应:返回激活病例集合。"""
items: list[CaseListItem]
class CasePatientInfo(BaseModel):
"""患者展示信息:用于病例详情页。"""
name: str | None = None
age: int | None = None
gender: str | None = None
occupation: str | None = None
class CaseDetailResponse(BaseModel):
"""病例详情响应:展示训练入口需要的信息。"""
id: int
case_code: str
title: str
department: str
difficulty: str
patient: CasePatientInfo
chief_complaint: str | None = None
supported_training_type: str
supported_mode: str
has_teaching_video: bool
has_knowledge_points: bool
has_quiz: bool
order_item_types: list[str]
class CaseDeletePreviewResponse(BaseModel):
"""病例删除预览:返回删除该病例会影响的业务数据数量。"""
case_id: int
case_title: str
can_delete: bool
affected: dict[str, int]
class CaseDeleteRequest(BaseModel):
"""病例删除请求:前端必须显式确认,并默认同时删除该病例训练数据。"""
confirm: bool = False
delete_training_data: bool = True
class CaseDeleteResponse(BaseModel):
"""病例删除结果:返回已删除的各表记录数量。"""
deleted: bool
case_id: int
deleted_counts: dict[str, int]
+84
View File
@@ -0,0 +1,84 @@
from datetime import datetime
from pydantic import BaseModel, Field
class CreateEvaluationRequest(BaseModel):
"""评价生成入参:指定输出分数类型。"""
score_type: str = Field(default="percentage", pattern="^(percentage|five_point)$")
class DimensionScore(BaseModel):
"""维度评分:保存单个评分维度的分数、满分和评价。"""
dimension: str
score: float
max_score: float
comment: str
evidence: list[str] = Field(default_factory=list)
deductions: list[str] = Field(default_factory=list)
improvement: str = ""
class ScoreDetailItem(BaseModel):
"""评分明细:对应 training_score_detail 的单条评分细则。"""
id: int | None = None
record_id: int | None = None
rule_id: int | None = None
dimension: str
score: float | None = None
deducted_reason: str | None = None
evidence_message_ids: list = Field(default_factory=list)
ai_confidence: float | None = None
comment: str | None = None
class EvaluationResponse(BaseModel):
"""评价报告响应:返回结构化 AI 评价报告。"""
evaluation_id: int
score_type: str
total_score: float
dimension_scores: list[DimensionScore]
score_details: list[ScoreDetailItem] = Field(default_factory=list)
errors: list[dict]
improvement_plan: list[str]
evidence_summary: list[str]
guideline_refs: list[dict]
overall_comment: str
class EvaluationListItem(BaseModel):
"""历史评价列表项:按 user_id 查询完整训练后的评价记录。"""
evaluation_id: int
case_title: str
score_type: str
total_score: float
created_at: datetime
pdf_exported: bool
class EvaluationListResponse(BaseModel):
"""历史评价列表响应。"""
items: list[EvaluationListItem]
class ExportPdfResponse(BaseModel):
"""PDF 导出响应:返回导出记录和本地文件路径。"""
export_id: int
file_path: str
class EvaluationDetailResponse(EvaluationResponse):
"""评价详情响应:在报告详情页使用。"""
session_id: int
case_id: int
case_title: str
created_at: datetime
pdf_file_path: str | None = None
+36
View File
@@ -0,0 +1,36 @@
from pydantic import BaseModel, Field
class CaseSqlPreviewCase(BaseModel):
"""病例 SQL 预览病例:展示导入文件中识别到的病例摘要。"""
id: int
title: str
case_type: str
difficulty: str
class CaseSqlImportPreviewResponse(BaseModel):
"""病例 SQL 预检响应:只展示解析结果,不写入数据库。"""
file_name: str
encoding: str | None = None
tables: dict[str, int] = Field(default_factory=dict)
can_import: bool = False
warnings: list[str] = Field(default_factory=list)
errors: list[str] = Field(default_factory=list)
preview_cases: list[CaseSqlPreviewCase] = Field(default_factory=list)
class CaseSqlImportApplyResponse(BaseModel):
"""病例 SQL 导入响应:展示实际写库结果。"""
imported: bool
file_name: str
encoding: str
inserted_or_updated_cases: int
imported_traditional_cases: int
imported_teaching_cases: int
imported_scoring_rules: int
generated_exam_items: int
warnings: list[str] = Field(default_factory=list)
+9
View File
@@ -0,0 +1,9 @@
from pydantic import BaseModel
class KnowledgeSearchResponse(BaseModel):
"""知识检索响应:返回评分参考指南片段和来源。"""
matched_chunks: list[dict]
source_refs: list[dict]
no_match: bool
+20
View File
@@ -0,0 +1,20 @@
from pydantic import BaseModel
class LLMTestRequest(BaseModel):
"""LLM 测试入参:用于快速模型和 reason 模型耗时验证。"""
message: str = "请用一句话说明医疗问诊训练 Demo 的用途。"
class LLMTestResponse(BaseModel):
"""LLM 测试响应:返回模型名、首 token 时间和总耗时。"""
model: str
first_token_ms: int | None = None
total_latency_ms: int
stream: bool
mock_mode: bool = False
fallback_used: bool = False
thinking_enabled: bool | None = None
reasoning_effort: str | None = None
+127
View File
@@ -0,0 +1,127 @@
from pydantic import BaseModel, Field, field_validator
class CreateSessionRequest(BaseModel):
"""创建会话入参:选择病例、训练类别、模式和分数类型。"""
case_id: int
training_type: str = Field(pattern="^(case_analysis|diagnosis_treatment|consultation)$")
mode: str = Field(pattern="^(novice|practice|teaching)$")
score_type: str = Field(default="percentage", pattern="^(percentage|five_point)$")
@field_validator("mode")
@classmethod
def normalize_mode(cls, value: str) -> str:
"""训练模式:兼容旧 novice 请求,实际按 practice 练习模式处理。"""
return "practice" if value == "novice" else value
class CreateSessionResponse(BaseModel):
"""创建会话响应:返回会话标识和 AI 病人开场白。"""
session_id: int
session_code: str
status: str
patient_opening: str
class ChatRequest(BaseModel):
"""问诊消息入参:医生向 AI 病人发送的自然语言问题。"""
message: str = Field(min_length=1, max_length=2000)
class ChatResponse(BaseModel):
"""问诊消息响应:返回 AI 病人的非流式回复。"""
reply: str
latency_ms: int
model: str
fallback_used: bool = False
class OrderItemResponse(BaseModel):
"""可申请检查项:只返回名称和类型,不返回结果。"""
item_code: str
item_name: str
item_type: str
class OrderItemsResponse(BaseModel):
"""可申请检查项列表响应。"""
items: list[OrderItemResponse]
class CreateOrderRequest(BaseModel):
"""检查申请入参:指定当前病例下的检查项目编码。"""
item_code: str = Field(min_length=1, max_length=64)
class CreateOrderResponse(BaseModel):
"""检查申请响应:返回数据库预设的结构化检查结果。"""
item_code: str
item_name: str
item_type: str
result_text: str
result_structured: dict | None = None
is_key: bool
is_abnormal: bool
context_written: bool = True
already_ordered: bool = False
class SessionStatusResponse(BaseModel):
"""会话状态响应:用于阶段流转接口。"""
session_id: int
status: str
class SubmitDiagnosisRequest(BaseModel):
"""诊断提交入参:保存主要诊断、鉴别诊断和诊断依据。"""
primary_diagnosis: str = Field(min_length=1, max_length=2000)
differential_diagnoses: list[str] = Field(default_factory=list)
diagnosis_basis: str = Field(min_length=1, max_length=5000)
class SubmitDiagnosisResponse(BaseModel):
"""诊断提交响应:进入治疗阶段。"""
status: str
class SubmitTreatmentRequest(BaseModel):
"""治疗方案入参:保存治疗原则、措施、风险预案、沟通和随访。"""
treatment_principle: str = Field(min_length=1, max_length=3000)
treatment_measures: str = Field(min_length=1, max_length=5000)
risk_plan: str | None = Field(default=None, max_length=3000)
communication: str | None = Field(default=None, max_length=3000)
follow_up: str | None = Field(default=None, max_length=3000)
class SubmitTreatmentResponse(BaseModel):
"""治疗提交响应:进入评价阶段。"""
status: str
class HintRequest(BaseModel):
"""会话提示入参:基于当前会话上下文生成新手模式提醒。"""
last_user_message: str | None = Field(default=None, max_length=2000)
scope: str = Field(default="current_conversation", pattern="^current_conversation$")
class HintResponse(BaseModel):
"""会话提示响应:返回缺失维度、下一步问题和轻量训练提示。"""
hints: list[str]
missing_dimensions: list[str]
next_questions: list[str]
recommended_orders: list[dict] = Field(default_factory=list)