feat: 新增多格式文件上传入库与认证体系

- 新增 JWT 认证模块,支持登录/注册/用户管理
- 新增文件上传接口,支持 .txt/.md/.html/.pdf/.docx 等格式解析入库
- 新增检索结果 AI 总结功能
- 新增文本去重缓存机制
- 新增全局认证夹具简化测试
- 新增配置项与环境变量支持
- 完善文档与测试覆盖
This commit is contained in:
2026-07-30 10:30:15 +08:00
parent 51dc8dc4f6
commit dce9e31bde
31 changed files with 3021 additions and 45 deletions
+42
View File
@@ -0,0 +1,42 @@
"""认证相关数据模型"""
from datetime import datetime
from pydantic import BaseModel, Field
class LoginRequest(BaseModel):
"""登录请求"""
username: str = Field(description="用户名")
password: str = Field(description="明文密码")
class RegisterRequest(BaseModel):
"""注册请求"""
username: str = Field(min_length=3, max_length=32, description="用户名(3-32 字符)")
password: str = Field(min_length=6, max_length=128, description="密码(6-128 字符)")
class AuthUser(BaseModel):
"""对外暴露的用户信息(不含密码)"""
username: str = Field(description="用户名")
role: str = Field(default="user", description="角色:admin | user")
created_at: datetime = Field(description="创建时间")
class StoredUser(AuthUser):
"""存储层用户:含密码哈希,仅内部使用,不对外暴露"""
hashed_password: str = Field(description="bcrypt 密码哈希")
class TokenResponse(BaseModel):
"""登录成功返回的 token 信息"""
access_token: str = Field(description="JWT access token")
token_type: str = Field(default="bearer", description="token 类型")
expires_in: int = Field(description="token 有效期(秒)")
user: AuthUser = Field(description="登录用户信息")
+1
View File
@@ -49,3 +49,4 @@ class IngestionResult(BaseModel):
chunks_count: int = Field(default=0, description="写入的 chunk 数量")
tags: list[str] = Field(default_factory=list, description="附加分类标签")
category_confidence: float = Field(default=0.0, description="主类目分类置信度")
deduplicated: bool = Field(default=False, description="是否命中去重复用旧文档(True 时 document_id 为既有文档 ID")
+14
View File
@@ -8,6 +8,7 @@ class SearchRequest(BaseModel):
query: str = Field(description="查询文本")
top_k: int | None = Field(default=None, description="返回结果数,为空时使用 settings.retrieval_final_k")
summarize: bool = Field(default=False, description="是否对检索结果生成 AI 总结")
class SearchHit(BaseModel):
@@ -21,6 +22,17 @@ class SearchHit(BaseModel):
doc_summary: str = Field(default="", description="L1 文档总结,仅用于上下文标注")
class ExtractedInfo(BaseModel):
"""AI 从 query 中提取的关键信息"""
rewrite: str = Field(default="", description="改写后的 query")
keywords: list[str] = Field(default_factory=list, description="关键词")
entities: list[str] = Field(default_factory=list, description="实体(人名/产品/技术等)")
intent: str = Field(default="", description="查询意图分类")
time_range: str = Field(default="", description="时间范围,空表示无")
categories: list[str] = Field(default_factory=list, description="命中类目名(含未达阈值的候选)")
class SearchResponse(BaseModel):
"""检索响应"""
@@ -28,3 +40,5 @@ class SearchResponse(BaseModel):
hits: list[SearchHit] = Field(default_factory=list, description="命中结果列表")
routed_categories: list[str] = Field(default_factory=list, description="query 路由命中的类目")
fallback: bool = Field(default=False, description="是否走了全库兜底路径")
extracted_info: ExtractedInfo | None = Field(default=None, description="AI 提取的 query 关键信息")
summary: str | None = Field(default=None, description="检索结果 AI 总结,仅 summarize=true 时返回")