Files
hunjing/backend/app/models/lorebook.py
T
2026-08-21 16:00:13 +08:00

75 lines
3.6 KiB
Python

"""世谱(Lorebook / World Info)模型。
兼容酒馆 character_book 结构 + 魂镜扩展(scope / bound_entity_id)。
纯逻辑(匹配 / 预算 / 分组)在 app.modules.lorebook,本文件仅持久化载体。
"""
from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_lorebook_entry_id, new_lorebook_id
from app.core.time import utc_now
from app.db.base import Base
class Lorebook(Base):
"""一本世谱:条目集合 + 扫描 / 预算参数。
``character_id`` 为空表示 global 作用域(跨人物共享);非空时按人物隔离。
"""
__tablename__ = "lorebooks"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_lorebook_id)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), index=True
)
name: Mapped[str] = mapped_column(String(200), nullable=False, default="")
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
scan_depth: Mapped[int | None] = mapped_column(Integer)
token_budget: Mapped[int | None] = mapped_column(Integer)
recursive_scanning: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
scope: Mapped[str] = mapped_column(String(40), nullable=False, default="character", index=True)
status: Mapped[str] = mapped_column(String(40), nullable=False, default="active", index=True)
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)
class LorebookEntry(Base):
"""单条世谱条目:keys 命中后注入 content。
``keys`` / ``secondary_keys`` 为字符串数组,落库存 JSON 文本(契约字典 §4)。
"""
__tablename__ = "lorebook_entries"
id: Mapped[str] = mapped_column(
String(80), primary_key=True, default=new_lorebook_entry_id
)
lorebook_id: Mapped[str] = mapped_column(
String(80), ForeignKey("lorebooks.id"), nullable=False, index=True
)
keys_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
content: Mapped[str] = mapped_column(Text, nullable=False, default="")
secondary_keys_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
constant: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
selective: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
case_sensitive: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
use_regex: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
position: Mapped[str] = mapped_column(String(40), nullable=False, default="after_char")
insertion_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=10)
comment: Mapped[str] = mapped_column(String(500), nullable=False, default="")
# 软引用河图世界实体(绑定触发);不加 FK,避免跨枢强耦合
bound_entity_id: Mapped[str | None] = mapped_column(String(80), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utc_now, onupdate=utc_now
)