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
|
|
|
|
|
|
|
2026-02-27 08:03:39 +00:00
|
|
|
|
from sqlalchemy import JSON, Column
|
2026-02-24 05:19:38 +00:00
|
|
|
|
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),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
2026-02-28 04:52:50 +00:00
|
|
|
|
[v0.7.0] Extended with monitoring fields for Dashboard statistics.
|
2026-02-24 05:19:38 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__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"),
|
2026-02-28 04:52:50 +00:00
|
|
|
|
Index("ix_chat_messages_tenant_template", "tenant_id", "prompt_template_id"),
|
|
|
|
|
|
Index("ix_chat_messages_tenant_intent", "tenant_id", "intent_rule_id"),
|
|
|
|
|
|
Index("ix_chat_messages_tenant_flow", "tenant_id", "flow_instance_id"),
|
2026-02-24 05:19:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
2026-02-28 04:52:50 +00:00
|
|
|
|
prompt_template_id: uuid.UUID | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[v0.7.0] ID of the Prompt template used",
|
|
|
|
|
|
foreign_key="prompt_templates.id",
|
|
|
|
|
|
)
|
|
|
|
|
|
intent_rule_id: uuid.UUID | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[v0.7.0] ID of the Intent rule that matched",
|
|
|
|
|
|
foreign_key="intent_rules.id",
|
|
|
|
|
|
)
|
|
|
|
|
|
flow_instance_id: uuid.UUID | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[v0.7.0] ID of the Flow instance if flow was active",
|
|
|
|
|
|
foreign_key="flow_instances.id",
|
|
|
|
|
|
)
|
|
|
|
|
|
guardrail_triggered: bool = Field(
|
|
|
|
|
|
default=False,
|
|
|
|
|
|
description="[v0.7.0] Whether output guardrail was triggered"
|
|
|
|
|
|
)
|
|
|
|
|
|
guardrail_words: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("guardrail_words", JSON, nullable=True),
|
|
|
|
|
|
description="[v0.7.0] Guardrail trigger details: words, categories, strategy"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-02-24 05:19:38 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2026-03-05 09:10:09 +00:00
|
|
|
|
class SharedSession(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDMP-SHARE] Shared session entity for dialogue sharing.
|
|
|
|
|
|
Allows sharing chat sessions via unique token with expiration and concurrent user limits.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "shared_sessions"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_shared_sessions_share_token", "share_token", unique=True),
|
|
|
|
|
|
Index("ix_shared_sessions_tenant_session", "tenant_id", "session_id"),
|
|
|
|
|
|
Index("ix_shared_sessions_expires_at", "expires_at"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
share_token: str = Field(..., description="Unique share token (UUID)", index=True)
|
|
|
|
|
|
session_id: str = Field(..., description="Associated session ID", index=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
|
title: str | None = Field(default=None, description="Share title")
|
|
|
|
|
|
description: str | None = Field(default=None, description="Share description")
|
|
|
|
|
|
expires_at: datetime = Field(..., description="Expiration time")
|
|
|
|
|
|
is_active: bool = Field(default=True, description="Whether share is active")
|
|
|
|
|
|
max_concurrent_users: int = Field(default=10, description="Maximum concurrent users allowed")
|
|
|
|
|
|
current_users: int = Field(default=0, description="Current number of online users")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-25 15:08:36 +00:00
|
|
|
|
class Tenant(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-10] Tenant entity for storing tenant information.
|
|
|
|
|
|
Tenant ID format: name@ash@year (e.g., szmp@ash@2026)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "tenants"
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Full tenant ID (format: name@ash@year)", unique=True, index=True)
|
|
|
|
|
|
name: str = Field(..., description="Tenant display name (first part of tenant_id)")
|
|
|
|
|
|
year: str = Field(..., description="Year part from tenant_id")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 06:15:10 +00:00
|
|
|
|
class KBType(str, Enum):
|
|
|
|
|
|
PRODUCT = "product"
|
|
|
|
|
|
FAQ = "faq"
|
|
|
|
|
|
SCRIPT = "script"
|
|
|
|
|
|
POLICY = "policy"
|
|
|
|
|
|
GENERAL = "general"
|
|
|
|
|
|
|
|
|
|
|
|
|
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 KnowledgeBase(SQLModel, table=True):
|
|
|
|
|
|
"""
|
2026-02-27 06:15:10 +00:00
|
|
|
|
[AC-ASA-01, AC-AISVC-59] Knowledge base entity with tenant isolation.
|
|
|
|
|
|
[v0.6.0] Extended with kb_type, priority, is_enabled, doc_count for multi-KB management.
|
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
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "knowledge_bases"
|
|
|
|
|
|
__table_args__ = (
|
2026-02-27 06:15:10 +00:00
|
|
|
|
Index("ix_knowledge_bases_tenant_kb_type", "tenant_id", "kb_type"),
|
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
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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")
|
2026-02-27 08:03:39 +00:00
|
|
|
|
kb_type: str = Field(
|
|
|
|
|
|
default=KBType.GENERAL.value,
|
|
|
|
|
|
description="Knowledge base type: product/faq/script/policy/general"
|
|
|
|
|
|
)
|
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
|
|
|
|
description: str | None = Field(default=None, description="Knowledge base description")
|
2026-02-27 06:15:10 +00:00
|
|
|
|
priority: int = Field(default=0, ge=0, description="Priority weight, higher value means higher priority")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="Whether the knowledge base is enabled")
|
|
|
|
|
|
doc_count: int = Field(default=0, ge=0, description="Document count (cached statistic)")
|
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
|
|
|
|
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."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
2026-02-27 06:15:10 +00:00
|
|
|
|
kb_type: str = KBType.GENERAL.value
|
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
|
|
|
|
description: str | None = None
|
2026-02-27 06:15:10 +00:00
|
|
|
|
priority: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class KnowledgeBaseUpdate(SQLModel):
|
|
|
|
|
|
"""Schema for updating a knowledge base."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
kb_type: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
priority: int | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
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 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
|
2026-02-25 18:52:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApiKey(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-50] API Key entity for lightweight authentication.
|
|
|
|
|
|
Keys are loaded into memory on startup for fast validation.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "api_keys"
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
key: str = Field(..., description="API Key (unique)", unique=True, index=True)
|
|
|
|
|
|
name: str = Field(..., description="Key name/description for identification")
|
|
|
|
|
|
is_active: bool = Field(default=True, description="Whether the key is active")
|
2026-03-05 17:10:42 +00:00
|
|
|
|
expires_at: datetime | None = Field(default=None, description="Expiration time; null means never expires")
|
|
|
|
|
|
allowed_ips: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("allowed_ips", JSON, nullable=True),
|
|
|
|
|
|
description="Optional IP allowlist for this key",
|
|
|
|
|
|
)
|
|
|
|
|
|
rate_limit_qpm: int | None = Field(default=60, description="Per-minute quota for this key")
|
2026-02-25 18:52:50 +00:00
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApiKeyCreate(SQLModel):
|
|
|
|
|
|
"""Schema for creating a new API key."""
|
|
|
|
|
|
|
|
|
|
|
|
key: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
is_active: bool = True
|
2026-03-05 17:10:42 +00:00
|
|
|
|
expires_at: datetime | None = None
|
|
|
|
|
|
allowed_ips: list[str] | None = None
|
|
|
|
|
|
rate_limit_qpm: int | None = 60
|
2026-02-27 06:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateVersionStatus(str, Enum):
|
|
|
|
|
|
DRAFT = "draft"
|
|
|
|
|
|
PUBLISHED = "published"
|
|
|
|
|
|
ARCHIVED = "archived"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PromptTemplate(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-51, AC-AISVC-52] Prompt template entity with tenant isolation.
|
|
|
|
|
|
Main table for storing template metadata.
|
2026-03-02 14:14:46 +00:00
|
|
|
|
[AC-IDSMETA-16] Extended with metadata field for unified storage structure.
|
2026-02-27 06:15:10 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "prompt_templates"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_prompt_templates_tenant_scene", "tenant_id", "scene"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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="Template name, e.g., 'Default Customer Service Persona'")
|
|
|
|
|
|
scene: str = Field(..., description="Scene tag: chat/rag_qa/greeting/farewell")
|
|
|
|
|
|
description: str | None = Field(default=None, description="Template description")
|
|
|
|
|
|
is_default: bool = Field(default=False, description="Whether this is the default template for the scene")
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("metadata", JSON, nullable=True),
|
|
|
|
|
|
description="[AC-IDSMETA-16] Structured metadata for the prompt template"
|
|
|
|
|
|
)
|
2026-02-27 06:15:10 +00:00
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PromptTemplateVersion(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-53] Prompt template version entity.
|
|
|
|
|
|
Stores versioned content with status management.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "prompt_template_versions"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_template_versions_template_status", "template_id", "status"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
2026-02-27 08:03:39 +00:00
|
|
|
|
template_id: uuid.UUID = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="Foreign key to prompt_templates.id",
|
|
|
|
|
|
foreign_key="prompt_templates.id",
|
|
|
|
|
|
index=True
|
|
|
|
|
|
)
|
2026-02-27 06:15:10 +00:00
|
|
|
|
version: int = Field(..., description="Version number (auto-incremented per template)")
|
2026-02-27 08:03:39 +00:00
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=TemplateVersionStatus.DRAFT.value,
|
|
|
|
|
|
description="Version status: draft/published/archived"
|
|
|
|
|
|
)
|
|
|
|
|
|
system_instruction: str = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="System instruction content with {{variable}} placeholders"
|
|
|
|
|
|
)
|
2026-02-27 06:15:10 +00:00
|
|
|
|
variables: list[dict[str, Any]] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("variables", JSON, nullable=True),
|
2026-02-27 08:03:39 +00:00
|
|
|
|
description="Variable definitions, e.g., [{'name': 'persona_name', 'default': '小N'}]"
|
2026-02-27 06:15:10 +00:00
|
|
|
|
)
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PromptTemplateCreate(SQLModel):
|
|
|
|
|
|
"""Schema for creating a new prompt template."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
scene: str
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
system_instruction: str
|
|
|
|
|
|
variables: list[dict[str, Any]] | None = None
|
|
|
|
|
|
is_default: bool = False
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 06:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PromptTemplateUpdate(SQLModel):
|
|
|
|
|
|
"""Schema for updating a prompt template."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
scene: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
system_instruction: str | None = None
|
|
|
|
|
|
variables: list[dict[str, Any]] | None = None
|
|
|
|
|
|
is_default: bool | None = None
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 06:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ResponseType(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-65] Response type for intent rules."""
|
|
|
|
|
|
FIXED = "fixed"
|
|
|
|
|
|
RAG = "rag"
|
|
|
|
|
|
FLOW = "flow"
|
|
|
|
|
|
TRANSFER = "transfer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IntentRule(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-65] Intent rule entity with tenant isolation.
|
|
|
|
|
|
Supports keyword and regex matching for intent recognition.
|
2026-03-02 14:14:46 +00:00
|
|
|
|
[AC-IDSMETA-16] Extended with metadata field for unified storage structure.
|
2026-02-27 06:15:10 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "intent_rules"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_intent_rules_tenant_enabled_priority", "tenant_id", "is_enabled"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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="Intent name, e.g., 'Return Intent'")
|
|
|
|
|
|
keywords: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("keywords", JSON, nullable=True),
|
|
|
|
|
|
description="Keyword list for matching, e.g., ['return', 'refund']"
|
|
|
|
|
|
)
|
|
|
|
|
|
patterns: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("patterns", JSON, nullable=True),
|
|
|
|
|
|
description="Regex pattern list for matching, e.g., ['return.*goods', 'how to return']"
|
|
|
|
|
|
)
|
|
|
|
|
|
priority: int = Field(default=0, description="Priority (higher value = higher priority)")
|
|
|
|
|
|
response_type: str = Field(..., description="Response type: fixed/rag/flow/transfer")
|
|
|
|
|
|
target_kb_ids: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("target_kb_ids", JSON, nullable=True),
|
|
|
|
|
|
description="Target knowledge base IDs for rag type"
|
|
|
|
|
|
)
|
|
|
|
|
|
flow_id: uuid.UUID | None = Field(default=None, description="Flow ID for flow type")
|
|
|
|
|
|
fixed_reply: str | None = Field(default=None, description="Fixed reply content for fixed type")
|
|
|
|
|
|
transfer_message: str | None = Field(default=None, description="Transfer message for transfer type")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="Whether the rule is enabled")
|
|
|
|
|
|
hit_count: int = Field(default=0, description="Hit count for statistics")
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("metadata", JSON, nullable=True),
|
|
|
|
|
|
description="[AC-IDSMETA-16] Structured metadata for the intent rule"
|
|
|
|
|
|
)
|
2026-02-27 06:15:10 +00:00
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IntentRuleCreate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-65] Schema for creating a new intent rule."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
keywords: list[str] | None = None
|
|
|
|
|
|
patterns: list[str] | None = None
|
|
|
|
|
|
priority: int = 0
|
|
|
|
|
|
response_type: str
|
|
|
|
|
|
target_kb_ids: list[str] | None = None
|
|
|
|
|
|
flow_id: str | None = None
|
|
|
|
|
|
fixed_reply: str | None = None
|
|
|
|
|
|
transfer_message: str | None = None
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 06:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IntentRuleUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-67] Schema for updating an intent rule."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
keywords: list[str] | None = None
|
|
|
|
|
|
patterns: list[str] | None = None
|
|
|
|
|
|
priority: int | None = None
|
|
|
|
|
|
response_type: str | None = None
|
|
|
|
|
|
target_kb_ids: list[str] | None = None
|
|
|
|
|
|
flow_id: str | None = None
|
|
|
|
|
|
fixed_reply: str | None = None
|
|
|
|
|
|
transfer_message: str | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 06:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IntentMatchResult:
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-69] Result of intent matching.
|
|
|
|
|
|
Contains the matched rule and match details.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
|
self,
|
|
|
|
|
|
rule: IntentRule,
|
|
|
|
|
|
match_type: str,
|
|
|
|
|
|
matched: str,
|
|
|
|
|
|
):
|
|
|
|
|
|
self.rule = rule
|
|
|
|
|
|
self.match_type = match_type
|
|
|
|
|
|
self.matched = matched
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"rule_id": str(self.rule.id),
|
|
|
|
|
|
"rule_name": self.rule.name,
|
|
|
|
|
|
"match_type": self.match_type,
|
|
|
|
|
|
"matched": self.matched,
|
|
|
|
|
|
"response_type": self.rule.response_type,
|
|
|
|
|
|
}
|
2026-02-27 07:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForbiddenWordCategory(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-78] Forbidden word category."""
|
|
|
|
|
|
COMPETITOR = "competitor"
|
|
|
|
|
|
SENSITIVE = "sensitive"
|
|
|
|
|
|
POLITICAL = "political"
|
|
|
|
|
|
CUSTOM = "custom"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForbiddenWordStrategy(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-78] Forbidden word replacement strategy."""
|
|
|
|
|
|
MASK = "mask"
|
|
|
|
|
|
REPLACE = "replace"
|
|
|
|
|
|
BLOCK = "block"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForbiddenWord(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-78] Forbidden word entity with tenant isolation.
|
|
|
|
|
|
Supports mask/replace/block strategies for output filtering.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "forbidden_words"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_forbidden_words_tenant_enabled", "tenant_id", "is_enabled"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
|
word: str = Field(..., description="Forbidden word to detect")
|
|
|
|
|
|
category: str = Field(..., description="Category: competitor/sensitive/political/custom")
|
|
|
|
|
|
strategy: str = Field(..., description="Replacement strategy: mask/replace/block")
|
|
|
|
|
|
replacement: str | None = Field(default=None, description="Replacement text for 'replace' strategy")
|
|
|
|
|
|
fallback_reply: str | None = Field(default=None, description="Fallback reply for 'block' strategy")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="Whether the word is enabled")
|
|
|
|
|
|
hit_count: int = Field(default=0, ge=0, description="Hit count for statistics")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForbiddenWordCreate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-78] Schema for creating a new forbidden word."""
|
|
|
|
|
|
|
|
|
|
|
|
word: str
|
|
|
|
|
|
category: str
|
|
|
|
|
|
strategy: str
|
|
|
|
|
|
replacement: str | None = None
|
|
|
|
|
|
fallback_reply: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ForbiddenWordUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-80] Schema for updating a forbidden word."""
|
|
|
|
|
|
|
|
|
|
|
|
word: str | None = None
|
|
|
|
|
|
category: str | None = None
|
|
|
|
|
|
strategy: str | None = None
|
|
|
|
|
|
replacement: str | None = None
|
|
|
|
|
|
fallback_reply: str | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BehaviorRuleCategory(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-84] Behavior rule category."""
|
|
|
|
|
|
COMPLIANCE = "compliance"
|
|
|
|
|
|
TONE = "tone"
|
|
|
|
|
|
BOUNDARY = "boundary"
|
|
|
|
|
|
CUSTOM = "custom"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BehaviorRule(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-84] Behavior rule entity with tenant isolation.
|
|
|
|
|
|
These rules are injected into Prompt system instruction as LLM behavior constraints.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "behavior_rules"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_behavior_rules_tenant_enabled", "tenant_id", "is_enabled"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
2026-02-27 08:03:39 +00:00
|
|
|
|
rule_text: str = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="Behavior constraint description, e.g., 'Do not promise specific compensation'"
|
|
|
|
|
|
)
|
2026-02-27 07:27:02 +00:00
|
|
|
|
category: str = Field(..., description="Category: compliance/tone/boundary/custom")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="Whether the rule is enabled")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BehaviorRuleCreate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-84] Schema for creating a new behavior rule."""
|
|
|
|
|
|
|
|
|
|
|
|
rule_text: str
|
|
|
|
|
|
category: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BehaviorRuleUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-85] Schema for updating a behavior rule."""
|
|
|
|
|
|
|
|
|
|
|
|
rule_text: str | None = None
|
|
|
|
|
|
category: str | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GuardrailResult:
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-82] Result of guardrail filtering.
|
|
|
|
|
|
Contains filtered reply and trigger information.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
|
self,
|
|
|
|
|
|
reply: str,
|
|
|
|
|
|
blocked: bool = False,
|
|
|
|
|
|
triggered_words: list[str] | None = None,
|
|
|
|
|
|
triggered_categories: list[str] | None = None,
|
|
|
|
|
|
):
|
|
|
|
|
|
self.reply = reply
|
|
|
|
|
|
self.blocked = blocked
|
|
|
|
|
|
self.triggered_words = triggered_words or []
|
|
|
|
|
|
self.triggered_categories = triggered_categories or []
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"reply": self.reply,
|
|
|
|
|
|
"blocked": self.blocked,
|
|
|
|
|
|
"triggered_words": self.triggered_words,
|
|
|
|
|
|
"triggered_categories": self.triggered_categories,
|
|
|
|
|
|
"guardrail_triggered": len(self.triggered_words) > 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InputScanResult:
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-83] Result of input scanning.
|
|
|
|
|
|
Contains flagged status and matched words (for logging only, no blocking).
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
|
self,
|
|
|
|
|
|
flagged: bool = False,
|
|
|
|
|
|
matched_words: list[str] | None = None,
|
|
|
|
|
|
matched_categories: list[str] | None = None,
|
|
|
|
|
|
):
|
|
|
|
|
|
self.flagged = flagged
|
|
|
|
|
|
self.matched_words = matched_words or []
|
|
|
|
|
|
self.matched_categories = matched_categories or []
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"input_flagged": self.flagged,
|
|
|
|
|
|
"matched_words": self.matched_words,
|
|
|
|
|
|
"matched_categories": self.matched_categories,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowInstanceStatus(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-74~AC-AISVC-77] Flow instance status."""
|
|
|
|
|
|
ACTIVE = "active"
|
|
|
|
|
|
COMPLETED = "completed"
|
|
|
|
|
|
TIMEOUT = "timeout"
|
|
|
|
|
|
CANCELLED = "cancelled"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TimeoutAction(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-71] Timeout action for flow steps."""
|
|
|
|
|
|
REPEAT = "repeat"
|
|
|
|
|
|
SKIP = "skip"
|
|
|
|
|
|
TRANSFER = "transfer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScriptFlow(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-71] Script flow entity with tenant isolation.
|
|
|
|
|
|
Stores flow definition with steps in JSONB format.
|
2026-03-02 14:14:46 +00:00
|
|
|
|
[AC-IDSMETA-16] Extended with metadata field for unified storage structure.
|
2026-02-27 07:27:02 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "script_flows"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_script_flows_tenant_enabled", "tenant_id", "is_enabled"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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="Flow name")
|
|
|
|
|
|
description: str | None = Field(default=None, description="Flow description")
|
|
|
|
|
|
steps: list[dict[str, Any]] = Field(
|
|
|
|
|
|
default=[],
|
|
|
|
|
|
sa_column=Column("steps", JSON, nullable=False),
|
2026-02-27 08:03:39 +00:00
|
|
|
|
description="Flow steps list with step_no, content, wait_input, timeout_seconds"
|
2026-02-27 07:27:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="Whether the flow is enabled")
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("metadata", JSON, nullable=True),
|
|
|
|
|
|
description="[AC-IDSMETA-16] Structured metadata for the script flow"
|
|
|
|
|
|
)
|
2026-02-27 07:27:02 +00:00
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowInstance(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-74] Flow instance entity for runtime state.
|
|
|
|
|
|
Tracks active flow execution per session.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "flow_instances"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_flow_instances_tenant_session", "tenant_id", "session_id"),
|
|
|
|
|
|
Index("ix_flow_instances_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)
|
|
|
|
|
|
session_id: str = Field(..., description="Session ID for conversation tracking", index=True)
|
2026-02-27 08:03:39 +00:00
|
|
|
|
flow_id: uuid.UUID = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="Foreign key to script_flows.id",
|
|
|
|
|
|
foreign_key="script_flows.id",
|
|
|
|
|
|
index=True
|
|
|
|
|
|
)
|
2026-02-27 07:27:02 +00:00
|
|
|
|
current_step: int = Field(default=1, ge=1, description="Current step number (1-indexed)")
|
2026-02-27 08:03:39 +00:00
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=FlowInstanceStatus.ACTIVE.value,
|
|
|
|
|
|
description="Instance status: active/completed/timeout/cancelled"
|
|
|
|
|
|
)
|
2026-02-27 07:27:02 +00:00
|
|
|
|
context: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("context", JSON, nullable=True),
|
2026-02-27 08:03:39 +00:00
|
|
|
|
description="Flow execution context, stores user inputs"
|
2026-02-27 07:27:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
started_at: datetime = Field(default_factory=datetime.utcnow, description="Instance start time")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update time")
|
|
|
|
|
|
completed_at: datetime | None = Field(default=None, description="Completion time (nullable)")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 14:14:46 +00:00
|
|
|
|
class ScriptMode(str, Enum):
|
|
|
|
|
|
"""[AC-IDS-01] Script generation mode for flow steps."""
|
|
|
|
|
|
FIXED = "fixed"
|
|
|
|
|
|
FLEXIBLE = "flexible"
|
|
|
|
|
|
TEMPLATE = "template"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 07:27:02 +00:00
|
|
|
|
class FlowStep(SQLModel):
|
2026-03-02 14:14:46 +00:00
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-71] Schema for a single flow step.
|
|
|
|
|
|
[AC-IDS-01] Extended with intent-driven script generation fields.
|
|
|
|
|
|
"""
|
2026-02-27 07:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
step_no: int = Field(..., ge=1, description="Step number (1-indexed)")
|
|
|
|
|
|
content: str = Field(..., description="Script content for this step")
|
|
|
|
|
|
wait_input: bool = Field(default=True, description="Whether to wait for user input")
|
|
|
|
|
|
timeout_seconds: int = Field(default=120, ge=1, description="Timeout in seconds")
|
2026-02-27 08:03:39 +00:00
|
|
|
|
timeout_action: str = Field(
|
|
|
|
|
|
default=TimeoutAction.REPEAT.value,
|
|
|
|
|
|
description="Action on timeout: repeat/skip/transfer"
|
|
|
|
|
|
)
|
2026-02-27 07:27:02 +00:00
|
|
|
|
next_conditions: list[dict[str, Any]] | None = Field(
|
|
|
|
|
|
default=None,
|
2026-02-27 08:03:39 +00:00
|
|
|
|
description="Conditions for next step: [{'keywords': [...], 'goto_step': N}]"
|
2026-02-27 07:27:02 +00:00
|
|
|
|
)
|
|
|
|
|
|
default_next: int | None = Field(default=None, description="Default next step if no condition matches")
|
|
|
|
|
|
|
2026-03-02 14:14:46 +00:00
|
|
|
|
script_mode: str = Field(
|
|
|
|
|
|
default=ScriptMode.FIXED.value,
|
|
|
|
|
|
description="[AC-IDS-01] Script mode: fixed/flexible/template"
|
|
|
|
|
|
)
|
|
|
|
|
|
intent: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[AC-IDS-01] Step intent for flexible mode (e.g., '获取用户姓名')"
|
|
|
|
|
|
)
|
|
|
|
|
|
intent_description: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[AC-IDS-01] Detailed intent description for better AI understanding"
|
|
|
|
|
|
)
|
|
|
|
|
|
script_constraints: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[AC-IDS-01] Script constraints for flexible mode (e.g., ['必须礼貌', '语气自然'])"
|
|
|
|
|
|
)
|
|
|
|
|
|
expected_variables: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="[AC-IDS-01] Expected variables to extract from user input"
|
|
|
|
|
|
)
|
|
|
|
|
|
rag_config: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="RAG configuration for this step: {'enabled': true, 'tag_filter': {'grade': '${context.grade}', 'type': '痛点'}}"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-02-27 07:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
class ScriptFlowCreate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-71] Schema for creating a new script flow."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
steps: list[dict[str, Any]]
|
|
|
|
|
|
is_enabled: bool = True
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 07:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScriptFlowUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-73] Schema for updating a script flow."""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
steps: list[dict[str, Any]] | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
2026-03-02 14:14:46 +00:00
|
|
|
|
metadata_: dict[str, Any] | None = None
|
2026-02-27 07:27:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowAdvanceResult:
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-75] Result of flow step advancement.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
|
self,
|
|
|
|
|
|
completed: bool,
|
|
|
|
|
|
message: str | None = None,
|
|
|
|
|
|
current_step: int | None = None,
|
|
|
|
|
|
total_steps: int | None = None,
|
|
|
|
|
|
timeout_action: str | None = None,
|
|
|
|
|
|
):
|
|
|
|
|
|
self.completed = completed
|
|
|
|
|
|
self.message = message
|
|
|
|
|
|
self.current_step = current_step
|
|
|
|
|
|
self.total_steps = total_steps
|
|
|
|
|
|
self.timeout_action = timeout_action
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
result = {
|
|
|
|
|
|
"completed": self.completed,
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.message is not None:
|
|
|
|
|
|
result["message"] = self.message
|
|
|
|
|
|
if self.current_step is not None:
|
|
|
|
|
|
result["current_step"] = self.current_step
|
|
|
|
|
|
if self.total_steps is not None:
|
|
|
|
|
|
result["total_steps"] = self.total_steps
|
|
|
|
|
|
if self.timeout_action is not None:
|
|
|
|
|
|
result["timeout_action"] = self.timeout_action
|
|
|
|
|
|
return result
|
2026-02-28 04:52:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowTestRecordStatus(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-93] Flow test record status."""
|
|
|
|
|
|
SUCCESS = "success"
|
|
|
|
|
|
PARTIAL = "partial"
|
|
|
|
|
|
FAILED = "failed"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowTestRecord(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-93] Flow test record entity for complete 12-step execution logging.
|
|
|
|
|
|
Records are retained for 7 days (TTL cleanup via background task).
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "flow_test_records"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_flow_test_records_tenant_created", "tenant_id", "created_at"),
|
|
|
|
|
|
Index("ix_flow_test_records_session", "session_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 test session")
|
|
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=FlowTestRecordStatus.SUCCESS.value,
|
|
|
|
|
|
description="Overall status: success/partial/failed"
|
|
|
|
|
|
)
|
|
|
|
|
|
steps: list[dict[str, Any]] = Field(
|
|
|
|
|
|
default=[],
|
|
|
|
|
|
sa_column=Column("steps", JSON, nullable=False),
|
|
|
|
|
|
description="12-step execution logs with step, name, status, duration_ms, input, output, error, metadata"
|
|
|
|
|
|
)
|
|
|
|
|
|
final_response: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("final_response", JSON, nullable=True),
|
|
|
|
|
|
description="Final ChatResponse with reply, confidence, should_transfer"
|
|
|
|
|
|
)
|
|
|
|
|
|
total_duration_ms: int | None = Field(default=None, description="Total execution time in milliseconds")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Record creation time", index=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FlowTestStepResult(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-93] Schema for a single step result in flow test."""
|
|
|
|
|
|
|
|
|
|
|
|
step: int = Field(..., description="Step number (1-12)")
|
|
|
|
|
|
name: str = Field(..., description="Step name")
|
|
|
|
|
|
status: str = Field(..., description="Execution status: success/failed/skipped")
|
|
|
|
|
|
duration_ms: int | None = Field(default=None, description="Execution time in milliseconds")
|
|
|
|
|
|
input: dict[str, Any] | None = Field(default=None, description="Step input data")
|
|
|
|
|
|
output: dict[str, Any] | None = Field(default=None, description="Step output data")
|
|
|
|
|
|
error: str | None = Field(default=None, description="Error message if failed")
|
|
|
|
|
|
step_metadata: dict[str, Any] | None = Field(default=None, description="Step metadata (matched rule, template, etc.)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportTaskStatus(str, Enum):
|
|
|
|
|
|
"""[AC-AISVC-110] Export task status."""
|
|
|
|
|
|
PROCESSING = "processing"
|
|
|
|
|
|
COMPLETED = "completed"
|
|
|
|
|
|
FAILED = "failed"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportTask(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-AISVC-110] Export task entity for conversation export.
|
|
|
|
|
|
Supports async export with file download.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "export_tasks"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_export_tasks_tenant_status", "tenant_id", "status"),
|
|
|
|
|
|
Index("ix_export_tasks_tenant_created", "tenant_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)
|
|
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=ExportTaskStatus.PROCESSING.value,
|
|
|
|
|
|
description="Task status: processing/completed/failed"
|
|
|
|
|
|
)
|
|
|
|
|
|
file_path: str | None = Field(default=None, description="Path to exported file")
|
|
|
|
|
|
file_name: str | None = Field(default=None, description="Generated file name")
|
|
|
|
|
|
file_size: int | None = Field(default=None, description="File size in bytes")
|
|
|
|
|
|
total_rows: int | None = Field(default=None, description="Total rows exported")
|
|
|
|
|
|
format: str = Field(default="json", description="Export format: json/csv")
|
|
|
|
|
|
filters: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("filters", JSON, nullable=True),
|
|
|
|
|
|
description="Export filters applied"
|
|
|
|
|
|
)
|
|
|
|
|
|
error_message: str | None = Field(default=None, description="Error message if failed")
|
|
|
|
|
|
expires_at: datetime | None = Field(default=None, description="File expiration time (for cleanup)")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="Task creation time")
|
|
|
|
|
|
completed_at: datetime | None = Field(default=None, description="Completion time")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportTaskCreate(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-110] Schema for creating an export task."""
|
|
|
|
|
|
|
|
|
|
|
|
format: str = "json"
|
|
|
|
|
|
filters: dict[str, Any] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConversationDetail(SQLModel):
|
|
|
|
|
|
"""[AC-AISVC-109] Schema for conversation detail with execution chain."""
|
|
|
|
|
|
|
|
|
|
|
|
conversation_id: uuid.UUID
|
|
|
|
|
|
session_id: str
|
|
|
|
|
|
user_message: str
|
|
|
|
|
|
ai_reply: str | None = None
|
|
|
|
|
|
triggered_rules: list[dict[str, Any]] | None = None
|
|
|
|
|
|
used_template: dict[str, Any] | None = None
|
|
|
|
|
|
used_flow: dict[str, Any] | None = None
|
|
|
|
|
|
execution_time_ms: int | None = None
|
|
|
|
|
|
confidence: float | None = None
|
|
|
|
|
|
should_transfer: bool = False
|
|
|
|
|
|
execution_steps: list[dict[str, Any]] | None = None
|
|
|
|
|
|
created_at: datetime
|
2026-03-02 14:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataFieldType(str, Enum):
|
|
|
|
|
|
"""元数据字段类型"""
|
|
|
|
|
|
STRING = "string"
|
|
|
|
|
|
NUMBER = "number"
|
|
|
|
|
|
BOOLEAN = "boolean"
|
|
|
|
|
|
ENUM = "enum"
|
|
|
|
|
|
ARRAY_ENUM = "array_enum"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-05 09:10:09 +00:00
|
|
|
|
class FieldRole(str, Enum):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-MRS-01] 字段角色枚举
|
|
|
|
|
|
用于标识元数据字段的职责分层
|
|
|
|
|
|
"""
|
|
|
|
|
|
RESOURCE_FILTER = "resource_filter"
|
|
|
|
|
|
SLOT = "slot"
|
|
|
|
|
|
PROMPT_VAR = "prompt_var"
|
|
|
|
|
|
ROUTING_SIGNAL = "routing_signal"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 14:14:46 +00:00
|
|
|
|
class MetadataFieldStatus(str, Enum):
|
|
|
|
|
|
"""[AC-IDSMETA-13] 元数据字段状态"""
|
|
|
|
|
|
DRAFT = "draft"
|
|
|
|
|
|
ACTIVE = "active"
|
|
|
|
|
|
DEPRECATED = "deprecated"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataScope(str, Enum):
|
|
|
|
|
|
"""[AC-IDSMETA-15] 元数据字段适用范围"""
|
|
|
|
|
|
KB_DOCUMENT = "kb_document"
|
|
|
|
|
|
INTENT_RULE = "intent_rule"
|
|
|
|
|
|
SCRIPT_FLOW = "script_flow"
|
|
|
|
|
|
PROMPT_TEMPLATE = "prompt_template"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataField(SQLModel):
|
|
|
|
|
|
"""元数据字段定义(非持久化,用于嵌套结构)"""
|
|
|
|
|
|
name: str = Field(..., description="字段名称,如 grade, subject, industry")
|
|
|
|
|
|
label: str = Field(..., description="字段显示名称,如 年级, 学科, 行业")
|
|
|
|
|
|
field_type: str = Field(
|
|
|
|
|
|
default=MetadataFieldType.STRING.value,
|
|
|
|
|
|
description="字段类型: string/number/boolean/enum/array_enum"
|
|
|
|
|
|
)
|
|
|
|
|
|
options: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="选项列表,用于 enum/array_enum 类型,如 ['初一', '初二', '初三']"
|
|
|
|
|
|
)
|
|
|
|
|
|
required: bool = Field(default=False, description="是否必填")
|
|
|
|
|
|
default_value: str | None = Field(default=None, description="默认值")
|
|
|
|
|
|
description: str | None = Field(default=None, description="字段描述")
|
|
|
|
|
|
sort_order: int = Field(default=0, description="排序顺序")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataFieldDefinition(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDSMETA-13] 元数据字段定义表
|
2026-03-05 09:10:09 +00:00
|
|
|
|
[AC-MRS-01,02,03] 支持字段角色分层配置
|
2026-03-02 14:14:46 +00:00
|
|
|
|
每个字段独立存储,支持字段级状态管理(draft/active/deprecated)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "metadata_field_definitions"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_metadata_field_definitions_tenant", "tenant_id"),
|
|
|
|
|
|
Index("ix_metadata_field_definitions_tenant_status", "tenant_id", "status"),
|
|
|
|
|
|
Index("ix_metadata_field_definitions_tenant_field_key", "tenant_id", "field_key", unique=True),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
|
field_key: str = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="字段键名,仅允许小写字母数字下划线,如 grade, subject, industry",
|
|
|
|
|
|
min_length=1,
|
|
|
|
|
|
max_length=64,
|
|
|
|
|
|
)
|
|
|
|
|
|
label: str = Field(..., description="字段显示名称", min_length=1, max_length=64)
|
|
|
|
|
|
type: str = Field(
|
|
|
|
|
|
default=MetadataFieldType.STRING.value,
|
|
|
|
|
|
description="字段类型: string/number/boolean/enum/array_enum"
|
|
|
|
|
|
)
|
|
|
|
|
|
required: bool = Field(default=False, description="是否必填")
|
|
|
|
|
|
options: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("options", JSON, nullable=True),
|
|
|
|
|
|
description="选项列表,用于 enum/array_enum 类型"
|
|
|
|
|
|
)
|
|
|
|
|
|
default_value: str | None = Field(default=None, description="默认值", sa_column=Column("default_value", JSON, nullable=True))
|
|
|
|
|
|
scope: list[str] = Field(
|
|
|
|
|
|
default_factory=lambda: [MetadataScope.KB_DOCUMENT.value],
|
|
|
|
|
|
sa_column=Column("scope", JSON, nullable=False),
|
|
|
|
|
|
description="适用范围: kb_document/intent_rule/script_flow/prompt_template"
|
|
|
|
|
|
)
|
|
|
|
|
|
is_filterable: bool = Field(default=True, description="是否可用于过滤")
|
|
|
|
|
|
is_rank_feature: bool = Field(default=False, description="是否用于排序特征")
|
2026-03-05 09:10:09 +00:00
|
|
|
|
field_roles: list[str] = Field(
|
|
|
|
|
|
default_factory=list,
|
|
|
|
|
|
sa_column=Column("field_roles", JSON, nullable=False, server_default="'[]'"),
|
|
|
|
|
|
description="[AC-MRS-01] 字段角色列表: resource_filter/slot/prompt_var/routing_signal"
|
|
|
|
|
|
)
|
2026-03-02 14:14:46 +00:00
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=MetadataFieldStatus.DRAFT.value,
|
|
|
|
|
|
description="字段状态: draft/active/deprecated"
|
|
|
|
|
|
)
|
|
|
|
|
|
version: int = Field(default=1, description="版本号")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataFieldDefinitionCreate(SQLModel):
|
2026-03-05 09:10:09 +00:00
|
|
|
|
"""[AC-IDSMETA-13] [AC-MRS-01] 创建元数据字段定义"""
|
2026-03-02 14:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
field_key: str = Field(..., min_length=1, max_length=64)
|
|
|
|
|
|
label: str = Field(..., min_length=1, max_length=64)
|
|
|
|
|
|
type: str = Field(default=MetadataFieldType.STRING.value)
|
|
|
|
|
|
required: bool = Field(default=False)
|
|
|
|
|
|
options: list[str] | None = None
|
|
|
|
|
|
default_value: str | int | float | bool | None = None
|
|
|
|
|
|
scope: list[str] = Field(default_factory=lambda: [MetadataScope.KB_DOCUMENT.value])
|
|
|
|
|
|
is_filterable: bool = Field(default=True)
|
|
|
|
|
|
is_rank_feature: bool = Field(default=False)
|
2026-03-05 09:10:09 +00:00
|
|
|
|
field_roles: list[str] = Field(default_factory=list)
|
2026-03-02 14:14:46 +00:00
|
|
|
|
status: str = Field(default=MetadataFieldStatus.DRAFT.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataFieldDefinitionUpdate(SQLModel):
|
2026-03-05 09:10:09 +00:00
|
|
|
|
"""[AC-IDSMETA-14] [AC-MRS-01] 更新元数据字段定义"""
|
2026-03-02 14:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
label: str | None = Field(default=None, min_length=1, max_length=64)
|
|
|
|
|
|
required: bool | None = None
|
|
|
|
|
|
options: list[str] | None = None
|
|
|
|
|
|
default_value: str | int | float | bool | None = None
|
|
|
|
|
|
scope: list[str] | None = None
|
|
|
|
|
|
is_filterable: bool | None = None
|
|
|
|
|
|
is_rank_feature: bool | None = None
|
2026-03-05 09:10:09 +00:00
|
|
|
|
field_roles: list[str] | None = None
|
2026-03-02 14:14:46 +00:00
|
|
|
|
status: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-05 09:10:09 +00:00
|
|
|
|
class ExtractStrategy(str, Enum):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-MRS-07] 槽位值提取策略
|
|
|
|
|
|
"""
|
|
|
|
|
|
RULE = "rule"
|
|
|
|
|
|
LLM = "llm"
|
|
|
|
|
|
USER_INPUT = "user_input"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SlotValueSource(str, Enum):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-MRS-09] 槽位值来源
|
|
|
|
|
|
"""
|
|
|
|
|
|
USER_CONFIRMED = "user_confirmed"
|
|
|
|
|
|
RULE_EXTRACTED = "rule_extracted"
|
|
|
|
|
|
LLM_INFERRED = "llm_inferred"
|
|
|
|
|
|
DEFAULT = "default"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SlotDefinition(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-MRS-07,08] 槽位定义表
|
|
|
|
|
|
独立的槽位定义模型,与元数据字段解耦但可复用
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "slot_definitions"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_slot_definitions_tenant", "tenant_id"),
|
|
|
|
|
|
Index("ix_slot_definitions_tenant_key", "tenant_id", "slot_key", unique=True),
|
|
|
|
|
|
Index("ix_slot_definitions_linked_field", "linked_field_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)
|
|
|
|
|
|
slot_key: str = Field(
|
|
|
|
|
|
...,
|
|
|
|
|
|
description="槽位键名,可与元数据字段 field_key 关联",
|
|
|
|
|
|
min_length=1,
|
|
|
|
|
|
max_length=100,
|
|
|
|
|
|
)
|
|
|
|
|
|
type: str = Field(
|
|
|
|
|
|
default=MetadataFieldType.STRING.value,
|
|
|
|
|
|
description="槽位类型: string/number/boolean/enum/array_enum"
|
|
|
|
|
|
)
|
|
|
|
|
|
required: bool = Field(default=False, description="是否必填槽位")
|
|
|
|
|
|
extract_strategy: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="提取策略: rule/llm/user_input"
|
|
|
|
|
|
)
|
|
|
|
|
|
validation_rule: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="校验规则(正则或 JSON Schema)"
|
|
|
|
|
|
)
|
|
|
|
|
|
ask_back_prompt: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="追问提示语模板"
|
|
|
|
|
|
)
|
|
|
|
|
|
default_value: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("default_value", JSON, nullable=True),
|
|
|
|
|
|
description="默认值"
|
|
|
|
|
|
)
|
|
|
|
|
|
linked_field_id: uuid.UUID | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="关联的元数据字段 ID",
|
|
|
|
|
|
foreign_key="metadata_field_definitions.id",
|
|
|
|
|
|
)
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SlotDefinitionCreate(SQLModel):
|
|
|
|
|
|
"""[AC-MRS-07,08] 创建槽位定义"""
|
|
|
|
|
|
|
|
|
|
|
|
slot_key: str = Field(..., min_length=1, max_length=100)
|
|
|
|
|
|
type: str = Field(default=MetadataFieldType.STRING.value)
|
|
|
|
|
|
required: bool = Field(default=False)
|
|
|
|
|
|
extract_strategy: str | None = None
|
|
|
|
|
|
validation_rule: str | None = None
|
|
|
|
|
|
ask_back_prompt: str | None = None
|
|
|
|
|
|
default_value: dict[str, Any] | None = None
|
|
|
|
|
|
linked_field_id: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SlotDefinitionUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-MRS-07] 更新槽位定义"""
|
|
|
|
|
|
|
|
|
|
|
|
type: str | None = None
|
|
|
|
|
|
required: bool | None = None
|
|
|
|
|
|
extract_strategy: str | None = None
|
|
|
|
|
|
validation_rule: str | None = None
|
|
|
|
|
|
ask_back_prompt: str | None = None
|
|
|
|
|
|
default_value: dict[str, Any] | None = None
|
|
|
|
|
|
linked_field_id: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SlotValue(SQLModel):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-MRS-09] 运行时槽位值
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
key: str = Field(..., description="槽位键名")
|
|
|
|
|
|
value: Any = Field(..., description="槽位值")
|
|
|
|
|
|
source: str = Field(
|
|
|
|
|
|
default=SlotValueSource.DEFAULT.value,
|
|
|
|
|
|
description="来源: user_confirmed/rule_extracted/llm_inferred/default"
|
|
|
|
|
|
)
|
|
|
|
|
|
confidence: float = Field(
|
|
|
|
|
|
default=1.0,
|
|
|
|
|
|
ge=0.0,
|
|
|
|
|
|
le=1.0,
|
|
|
|
|
|
description="置信度 0.0~1.0"
|
|
|
|
|
|
)
|
|
|
|
|
|
updated_at: datetime = Field(
|
|
|
|
|
|
default_factory=datetime.utcnow,
|
|
|
|
|
|
description="最后更新时间"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 14:14:46 +00:00
|
|
|
|
class MetadataSchema(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
元数据模式定义(保留兼容性)
|
|
|
|
|
|
每个租户可以定义自己的元数据字段配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "metadata_schemas"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_metadata_schemas_tenant", "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="模式名称,如 教育行业元数据")
|
|
|
|
|
|
description: str | None = Field(default=None, description="模式描述")
|
|
|
|
|
|
fields: list[dict[str, Any]] = Field(
|
|
|
|
|
|
default=[],
|
|
|
|
|
|
sa_column=Column("fields", JSON, nullable=False),
|
|
|
|
|
|
description="字段定义列表"
|
|
|
|
|
|
)
|
|
|
|
|
|
is_default: bool = Field(default=False, description="是否为租户默认模式")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="是否启用")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataSchemaCreate(SQLModel):
|
|
|
|
|
|
"""创建元数据模式"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
fields: list[dict[str, Any]]
|
|
|
|
|
|
is_default: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataSchemaUpdate(SQLModel):
|
|
|
|
|
|
"""更新元数据模式"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
fields: list[dict[str, Any]] | None = None
|
|
|
|
|
|
is_default: bool | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MetadataFieldCreate(SQLModel):
|
|
|
|
|
|
"""创建元数据字段"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
label: str
|
|
|
|
|
|
field_type: str = "string"
|
|
|
|
|
|
options: list[str] | None = None
|
|
|
|
|
|
required: bool = False
|
|
|
|
|
|
default_value: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
sort_order: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionTemplateStatus(str, Enum):
|
|
|
|
|
|
"""[AC-IDSMETA-22] 拆解模板状态"""
|
|
|
|
|
|
DRAFT = "draft"
|
|
|
|
|
|
PUBLISHED = "published"
|
|
|
|
|
|
ARCHIVED = "archived"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionTemplate(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDSMETA-22] 拆解模板表
|
|
|
|
|
|
用于将待录入文本按固定模板拆解为结构化数据
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "decomposition_templates"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_decomposition_templates_tenant", "tenant_id"),
|
|
|
|
|
|
Index("ix_decomposition_templates_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)
|
|
|
|
|
|
name: str = Field(..., description="模板名称")
|
|
|
|
|
|
description: str | None = Field(default=None, description="模板描述")
|
|
|
|
|
|
version: int = Field(default=1, description="版本号")
|
|
|
|
|
|
status: str = Field(
|
|
|
|
|
|
default=DecompositionTemplateStatus.DRAFT.value,
|
|
|
|
|
|
description="模板状态: draft/published/archived"
|
|
|
|
|
|
)
|
|
|
|
|
|
template_schema: dict[str, Any] = Field(
|
|
|
|
|
|
default_factory=dict,
|
|
|
|
|
|
sa_column=Column("template_schema", JSON, nullable=False),
|
|
|
|
|
|
description="输出模板结构定义,包含字段名、类型、描述等"
|
|
|
|
|
|
)
|
|
|
|
|
|
extraction_hints: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("extraction_hints", JSON, nullable=True),
|
|
|
|
|
|
description="提取提示,用于指导 LLM 提取特定字段"
|
|
|
|
|
|
)
|
|
|
|
|
|
example_input: str | None = Field(default=None, description="示例输入文本")
|
|
|
|
|
|
example_output: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("example_output", JSON, nullable=True),
|
|
|
|
|
|
description="示例输出 JSON"
|
|
|
|
|
|
)
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionTemplateCreate(SQLModel):
|
|
|
|
|
|
"""[AC-IDSMETA-22] 创建拆解模板"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
template_schema: dict[str, Any]
|
|
|
|
|
|
extraction_hints: dict[str, Any] | None = None
|
|
|
|
|
|
example_input: str | None = None
|
|
|
|
|
|
example_output: dict[str, Any] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionTemplateUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-IDSMETA-22] 更新拆解模板"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str | None = None
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
template_schema: dict[str, Any] | None = None
|
|
|
|
|
|
extraction_hints: dict[str, Any] | None = None
|
|
|
|
|
|
example_input: str | None = None
|
|
|
|
|
|
example_output: dict[str, Any] | None = None
|
|
|
|
|
|
status: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionRequest(SQLModel):
|
|
|
|
|
|
"""[AC-IDSMETA-21] 拆解请求"""
|
|
|
|
|
|
|
|
|
|
|
|
text: str = Field(..., description="待拆解的文本")
|
|
|
|
|
|
template_id: str | None = Field(default=None, description="指定模板 ID(可选)")
|
|
|
|
|
|
hints: dict[str, Any] | None = Field(default=None, description="额外提取提示")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DecompositionResult(SQLModel):
|
|
|
|
|
|
"""[AC-IDSMETA-21] 拆解结果"""
|
|
|
|
|
|
|
|
|
|
|
|
success: bool = Field(..., description="是否成功")
|
|
|
|
|
|
data: dict[str, Any] | None = Field(default=None, description="拆解后的结构化数据")
|
|
|
|
|
|
template_id: str | None = Field(default=None, description="使用的模板 ID")
|
|
|
|
|
|
template_version: int | None = Field(default=None, description="使用的模板版本")
|
|
|
|
|
|
confidence: float | None = Field(default=None, description="拆解置信度")
|
|
|
|
|
|
error: str | None = Field(default=None, description="错误信息")
|
|
|
|
|
|
latency_ms: int | None = Field(default=None, description="处理耗时(毫秒)")
|
2026-03-05 09:10:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HighRiskScenarioType(str, Enum):
|
|
|
|
|
|
"""[AC-IDMP-20] 高风险场景类型"""
|
|
|
|
|
|
REFUND = "refund"
|
|
|
|
|
|
COMPLAINT_ESCALATION = "complaint_escalation"
|
|
|
|
|
|
PRIVACY_SENSITIVE_PROMISE = "privacy_sensitive_promise"
|
|
|
|
|
|
TRANSFER = "transfer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HighRiskPolicy(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDMP-20] 高风险场景策略配置
|
|
|
|
|
|
定义高风险场景的最小集,支持动态配置
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "high_risk_policies"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_high_risk_policies_tenant", "tenant_id"),
|
|
|
|
|
|
Index("ix_high_risk_policies_tenant_enabled", "tenant_id", "is_enabled"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
|
|
|
|
tenant_id: str = Field(..., description="Tenant ID for multi-tenant isolation", index=True)
|
|
|
|
|
|
scenario: str = Field(..., description="场景类型: refund/complaint_escalation/privacy_sensitive_promise/transfer")
|
|
|
|
|
|
handler_mode: str = Field(
|
|
|
|
|
|
default="micro_flow",
|
|
|
|
|
|
description="处理模式: micro_flow/transfer"
|
|
|
|
|
|
)
|
|
|
|
|
|
flow_id: uuid.UUID | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="微流程ID (handler_mode=micro_flow时使用)",
|
|
|
|
|
|
foreign_key="script_flows.id"
|
|
|
|
|
|
)
|
|
|
|
|
|
transfer_message: str | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="转人工消息 (handler_mode=transfer时使用)"
|
|
|
|
|
|
)
|
|
|
|
|
|
keywords: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("keywords", JSON, nullable=True),
|
|
|
|
|
|
description="触发关键词列表"
|
|
|
|
|
|
)
|
|
|
|
|
|
patterns: list[str] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("patterns", JSON, nullable=True),
|
|
|
|
|
|
description="触发正则模式列表"
|
|
|
|
|
|
)
|
|
|
|
|
|
priority: int = Field(default=0, description="优先级 (值越高优先级越高)")
|
|
|
|
|
|
is_enabled: bool = Field(default=True, description="是否启用")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HighRiskPolicyCreate(SQLModel):
|
|
|
|
|
|
"""[AC-IDMP-20] 创建高风险策略"""
|
|
|
|
|
|
|
|
|
|
|
|
scenario: str
|
|
|
|
|
|
handler_mode: str = "micro_flow"
|
|
|
|
|
|
flow_id: str | None = None
|
|
|
|
|
|
transfer_message: str | None = None
|
|
|
|
|
|
keywords: list[str] | None = None
|
|
|
|
|
|
patterns: list[str] | None = None
|
|
|
|
|
|
priority: int = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HighRiskPolicyUpdate(SQLModel):
|
|
|
|
|
|
"""[AC-IDMP-20] 更新高风险策略"""
|
|
|
|
|
|
|
|
|
|
|
|
handler_mode: str | None = None
|
|
|
|
|
|
flow_id: str | None = None
|
|
|
|
|
|
transfer_message: str | None = None
|
|
|
|
|
|
keywords: list[str] | None = None
|
|
|
|
|
|
patterns: list[str] | None = None
|
|
|
|
|
|
priority: int | None = None
|
|
|
|
|
|
is_enabled: bool | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionModeRecord(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDMP-09] 会话模式记录
|
|
|
|
|
|
记录会话的当前模式状态
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "session_mode_records"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_session_mode_records_tenant_session", "tenant_id", "session_id", unique=True),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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="会话ID", index=True)
|
|
|
|
|
|
mode: str = Field(
|
|
|
|
|
|
default="BOT_ACTIVE",
|
|
|
|
|
|
description="会话模式: BOT_ACTIVE/HUMAN_ACTIVE"
|
|
|
|
|
|
)
|
|
|
|
|
|
reason: str | None = Field(default=None, description="模式切换原因")
|
|
|
|
|
|
switched_at: datetime | None = Field(default=None, description="模式切换时间")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间")
|
|
|
|
|
|
updated_at: datetime = Field(default_factory=datetime.utcnow, description="更新时间")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MidAuditLog(SQLModel, table=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
[AC-IDMP-07] 中台审计日志
|
|
|
|
|
|
记录 generation/request 维度的审计字段
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "mid_audit_logs"
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
Index("ix_mid_audit_logs_tenant_session", "tenant_id", "session_id"),
|
|
|
|
|
|
Index("ix_mid_audit_logs_tenant_request", "tenant_id", "request_id"),
|
|
|
|
|
|
Index("ix_mid_audit_logs_tenant_generation", "tenant_id", "generation_id"),
|
|
|
|
|
|
Index("ix_mid_audit_logs_created", "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="会话ID", index=True)
|
|
|
|
|
|
request_id: str = Field(..., description="请求ID", index=True)
|
|
|
|
|
|
generation_id: str = Field(..., description="生成ID", index=True)
|
|
|
|
|
|
mode: str = Field(..., description="执行模式: agent/micro_flow/fixed/transfer")
|
|
|
|
|
|
intent: str | None = Field(default=None, description="意图")
|
|
|
|
|
|
tool_calls: list[dict[str, Any]] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
sa_column=Column("tool_calls", JSON, nullable=True),
|
|
|
|
|
|
description="工具调用记录"
|
|
|
|
|
|
)
|
|
|
|
|
|
guardrail_triggered: bool = Field(default=False, description="护栏是否触发")
|
|
|
|
|
|
fallback_reason_code: str | None = Field(default=None, description="降级原因码")
|
|
|
|
|
|
react_iterations: int | None = Field(default=None, description="ReAct循环次数")
|
|
|
|
|
|
high_risk_scenario: str | None = Field(default=None, description="触发的高风险场景")
|
|
|
|
|
|
latency_ms: int | None = Field(default=None, description="总耗时(ms)")
|
|
|
|
|
|
created_at: datetime = Field(default_factory=datetime.utcnow, description="创建时间", index=True)
|