2026-02-24 05:19:38 +00:00
|
|
|
"""
|
|
|
|
|
Memory layer entities for AI Service.
|
|
|
|
|
[AC-AISVC-13] SQLModel entities for chat sessions and messages with tenant isolation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
from datetime import datetime
|
feat(ai-service): v0.2.0 前后端联调真实对接
实现内容:
- 新增知识库实体模型 (KnowledgeBase, Document, IndexJob)
- 新增 KBService 服务层,支持文档上传、存储、索引任务管理
- 实现知识库管理 API 真实对接 (POST/GET /admin/kb/documents)
- 实现索引任务状态查询 API (GET /admin/kb/index/jobs/{jobId})
- 实现 RAG 实验室真实向量检索 (POST /admin/rag/experiments/run)
- 实现会话监控真实数据库查询 (GET /admin/sessions)
规范更新:
- requirements.md: v0.1.0 -> v0.2.0, 新增 AC-AISVC-21~28
- tasks.md: v0.1.0 -> v0.2.0, 新增 Phase 6 (9个任务)
- openapi.admin.yaml: L0 -> L1, 更新 x-requirements 映射
验收标准: AC-AISVC-21, AC-AISVC-22, AC-AISVC-23, AC-AISVC-24,
AC-AISVC-25, AC-AISVC-26, AC-AISVC-27, AC-AISVC-28
2026-02-24 10:16:29 +00:00
|
|
|
from enum import Enum
|
2026-02-24 05:19:38 +00:00
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import Column, JSON
|
|
|
|
|
from sqlmodel import Field, Index, SQLModel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatSession(SQLModel, table=True):
|
|
|
|
|
"""
|
|
|
|
|
[AC-AISVC-13] Chat session entity with tenant isolation.
|
|
|
|
|
Primary key: (tenant_id, session_id) composite unique constraint.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "chat_sessions"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
Index("ix_chat_sessions_tenant_session", "tenant_id", "session_id", unique=True),
|
|
|
|
|
Index("ix_chat_sessions_tenant_id", "tenant_id"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
session_id: str = Field(..., description="Session ID for conversation tracking")
|
|
|
|
|
channel_type: str | None = Field(default=None, description="Channel type: wechat, douyin, jd")
|
|
|
|
|
metadata_: dict[str, Any] | None = Field(
|
|
|
|
|
default=None,
|
|
|
|
|
sa_column=Column("metadata", JSON, nullable=True),
|
|
|
|
|
description="Session metadata"
|
|
|
|
|
)
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Session creation time")
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatMessage(SQLModel, table=True):
|
|
|
|
|
"""
|
|
|
|
|
[AC-AISVC-13] Chat message entity with tenant isolation.
|
|
|
|
|
Messages are scoped by (tenant_id, session_id) for multi-tenant security.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "chat_messages"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
Index("ix_chat_messages_tenant_session", "tenant_id", "session_id"),
|
|
|
|
|
Index("ix_chat_messages_tenant_session_created", "tenant_id", "session_id", "created_at"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
session_id: str = Field(..., description="Session ID for conversation tracking", index=True)
|
|
|
|
|
role: str = Field(..., description="Message role: user or assistant")
|
|
|
|
|
content: str = Field(..., description="Message content")
|
2026-02-25 06:06:37 +00:00
|
|
|
prompt_tokens: int | None = Field(default=None, description="Number of prompt tokens used")
|
|
|
|
|
completion_tokens: int | None = Field(default=None, description="Number of completion tokens used")
|
|
|
|
|
total_tokens: int | None = Field(default=None, description="Total tokens used")
|
|
|
|
|
latency_ms: int | None = Field(default=None, description="Response latency in milliseconds")
|
|
|
|
|
first_token_ms: int | None = Field(default=None, description="Time to first token in milliseconds (for streaming)")
|
|
|
|
|
is_error: bool = Field(default=False, description="Whether this message is an error response")
|
|
|
|
|
error_message: str | None = Field(default=None, description="Error message if any")
|
2026-02-24 05:19:38 +00:00
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Message creation time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatSessionCreate(SQLModel):
|
|
|
|
|
"""Schema for creating a new chat session."""
|
|
|
|
|
|
|
|
|
|
tenant_id: str
|
|
|
|
|
session_id: str
|
|
|
|
|
channel_type: str | None = None
|
|
|
|
|
metadata_: dict[str, Any] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatMessageCreate(SQLModel):
|
|
|
|
|
"""Schema for creating a new chat message."""
|
|
|
|
|
|
|
|
|
|
tenant_id: str
|
|
|
|
|
session_id: str
|
|
|
|
|
role: str
|
|
|
|
|
content: str
|
feat(ai-service): v0.2.0 前后端联调真实对接
实现内容:
- 新增知识库实体模型 (KnowledgeBase, Document, IndexJob)
- 新增 KBService 服务层,支持文档上传、存储、索引任务管理
- 实现知识库管理 API 真实对接 (POST/GET /admin/kb/documents)
- 实现索引任务状态查询 API (GET /admin/kb/index/jobs/{jobId})
- 实现 RAG 实验室真实向量检索 (POST /admin/rag/experiments/run)
- 实现会话监控真实数据库查询 (GET /admin/sessions)
规范更新:
- requirements.md: v0.1.0 -> v0.2.0, 新增 AC-AISVC-21~28
- tasks.md: v0.1.0 -> v0.2.0, 新增 Phase 6 (9个任务)
- openapi.admin.yaml: L0 -> L1, 更新 x-requirements 映射
验收标准: AC-AISVC-21, AC-AISVC-22, AC-AISVC-23, AC-AISVC-24,
AC-AISVC-25, AC-AISVC-26, AC-AISVC-27, AC-AISVC-28
2026-02-24 10:16:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class DocumentStatus(str, Enum):
|
|
|
|
|
PENDING = "pending"
|
|
|
|
|
PROCESSING = "processing"
|
|
|
|
|
COMPLETED = "completed"
|
|
|
|
|
FAILED = "failed"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IndexJobStatus(str, Enum):
|
|
|
|
|
PENDING = "pending"
|
|
|
|
|
PROCESSING = "processing"
|
|
|
|
|
COMPLETED = "completed"
|
|
|
|
|
FAILED = "failed"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionStatus(str, Enum):
|
|
|
|
|
ACTIVE = "active"
|
|
|
|
|
CLOSED = "closed"
|
|
|
|
|
EXPIRED = "expired"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KnowledgeBase(SQLModel, table=True):
|
|
|
|
|
"""
|
|
|
|
|
[AC-ASA-01] Knowledge base entity with tenant isolation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "knowledge_bases"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
Index("ix_knowledge_bases_tenant_id", "tenant_id"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
name: str = Field(..., description="Knowledge base name")
|
|
|
|
|
description: str | None = Field(default=None, description="Knowledge base description")
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Document(SQLModel, table=True):
|
|
|
|
|
"""
|
|
|
|
|
[AC-ASA-01, AC-ASA-08] Document entity with tenant isolation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "documents"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
Index("ix_documents_tenant_kb", "tenant_id", "kb_id"),
|
|
|
|
|
Index("ix_documents_tenant_status", "tenant_id", "status"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
kb_id: str = Field(..., description="Knowledge base ID")
|
|
|
|
|
file_name: str = Field(..., description="Original file name")
|
|
|
|
|
file_path: str | None = Field(default=None, description="Storage path")
|
|
|
|
|
file_size: int | None = Field(default=None, description="File size in bytes")
|
|
|
|
|
file_type: str | None = Field(default=None, description="File MIME type")
|
|
|
|
|
status: str = Field(default=DocumentStatus.PENDING.value, description="Document status")
|
|
|
|
|
error_msg: str | None = Field(default=None, description="Error message if failed")
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Upload time")
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IndexJob(SQLModel, table=True):
|
|
|
|
|
"""
|
|
|
|
|
[AC-ASA-02] Index job entity for tracking document indexing progress.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "index_jobs"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
Index("ix_index_jobs_tenant_doc", "tenant_id", "doc_id"),
|
|
|
|
|
Index("ix_index_jobs_tenant_status", "tenant_id", "status"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
doc_id: uuid.UUID = Field(..., description="Document ID being indexed")
|
|
|
|
|
status: str = Field(default=IndexJobStatus.PENDING.value, description="Job status")
|
|
|
|
|
progress: int = Field(default=0, ge=0, le=100, description="Progress percentage")
|
|
|
|
|
error_msg: str | None = Field(default=None, description="Error message if failed")
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Job creation time")
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KnowledgeBaseCreate(SQLModel):
|
|
|
|
|
"""Schema for creating a new knowledge base."""
|
|
|
|
|
|
|
|
|
|
tenant_id: str
|
|
|
|
|
name: str
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DocumentCreate(SQLModel):
|
|
|
|
|
"""Schema for creating a new document."""
|
|
|
|
|
|
|
|
|
|
tenant_id: str
|
|
|
|
|
kb_id: str
|
|
|
|
|
file_name: str
|
|
|
|
|
file_path: str | None = None
|
|
|
|
|
file_size: int | None = None
|
|
|
|
|
file_type: str | None = None
|