Add brain memory services and APIs
Introduce the backend pieces for brain memory ingestion, routing, and system telemetry so the new knowledge workflows can project data into a brain view. The supporting tests lock in the new behavior and keep the expanded backend surface stable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,15 @@ from app.models.agent import Agent, AgentMessage
|
||||
from app.models.conversation import Conversation, Message
|
||||
from app.models.knowledge_graph import KGNode, KGEdge
|
||||
from app.models.memory import MemorySummary, UserMemory
|
||||
from app.models.brain import (
|
||||
BrainEvent,
|
||||
BrainCandidate,
|
||||
BrainMemory,
|
||||
BrainTag,
|
||||
brain_event_tags,
|
||||
brain_memory_tags,
|
||||
brain_memory_sources,
|
||||
)
|
||||
from app.models.todo import DailyTodo, TodoSource
|
||||
from app.models.log import Log, LogType, LogLevel
|
||||
|
||||
@@ -27,6 +36,13 @@ __all__ = [
|
||||
"KGEdge",
|
||||
"MemorySummary",
|
||||
"UserMemory",
|
||||
"BrainEvent",
|
||||
"BrainCandidate",
|
||||
"BrainMemory",
|
||||
"BrainTag",
|
||||
"brain_event_tags",
|
||||
"brain_memory_tags",
|
||||
"brain_memory_sources",
|
||||
"DailyTodo",
|
||||
"TodoSource",
|
||||
"Log",
|
||||
|
||||
93
backend/app/models/brain.py
Normal file
93
backend/app/models/brain.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, String, Table, Text
|
||||
from sqlalchemy.dialects.sqlite import JSON
|
||||
|
||||
from app.database import Base
|
||||
from app.models.base import BaseModel, utc_now
|
||||
|
||||
|
||||
brain_event_tags = Table(
|
||||
"brain_event_tags",
|
||||
Base.metadata,
|
||||
Column("event_id", String(36), ForeignKey("brain_events.id"), primary_key=True),
|
||||
Column("tag_id", String(36), ForeignKey("brain_tags.id"), primary_key=True),
|
||||
)
|
||||
|
||||
brain_memory_tags = Table(
|
||||
"brain_memory_tags",
|
||||
Base.metadata,
|
||||
Column("memory_id", String(36), ForeignKey("brain_memories.id"), primary_key=True),
|
||||
Column("tag_id", String(36), ForeignKey("brain_tags.id"), primary_key=True),
|
||||
)
|
||||
|
||||
brain_memory_sources = Table(
|
||||
"brain_memory_sources",
|
||||
Base.metadata,
|
||||
Column("memory_id", String(36), ForeignKey("brain_memories.id"), primary_key=True),
|
||||
Column("event_id", String(36), ForeignKey("brain_events.id"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
class BrainEvent(BaseModel):
|
||||
__tablename__ = "brain_events"
|
||||
|
||||
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
|
||||
source_type = Column(String(50), nullable=False, index=True)
|
||||
source_id = Column(String(36), nullable=False, index=True)
|
||||
event_type = Column(String(50), nullable=False, index=True)
|
||||
title = Column(String(255), nullable=True)
|
||||
content_summary = Column(Text, nullable=True)
|
||||
raw_excerpt = Column(Text, nullable=True)
|
||||
metadata_ = Column(JSON, nullable=True)
|
||||
importance_signal = Column(Float, default=0.0, nullable=False)
|
||||
is_user_pinned = Column(Integer, default=0, nullable=False)
|
||||
occurred_at = Column(DateTime, default=utc_now, nullable=False, index=True)
|
||||
processed_at = Column(DateTime, nullable=True)
|
||||
status = Column(String(20), default="pending", nullable=False, index=True)
|
||||
|
||||
|
||||
class BrainCandidate(BaseModel):
|
||||
__tablename__ = "brain_candidates"
|
||||
|
||||
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
|
||||
candidate_type = Column(String(50), nullable=False, index=True)
|
||||
title = Column(String(255), nullable=False)
|
||||
summary = Column(Text, nullable=False)
|
||||
importance_score = Column(Float, default=0.0, nullable=False)
|
||||
confidence_score = Column(Float, default=0.0, nullable=False)
|
||||
time_scope = Column(String(20), default="short_term", nullable=False)
|
||||
valid_from = Column(DateTime, nullable=True)
|
||||
valid_to = Column(DateTime, nullable=True)
|
||||
source_event_ids = Column(JSON, nullable=True)
|
||||
reasoning_trace = Column(Text, nullable=True)
|
||||
status = Column(String(20), default="new", nullable=False, index=True)
|
||||
reviewed_at = Column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class BrainMemory(BaseModel):
|
||||
__tablename__ = "brain_memories"
|
||||
|
||||
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
|
||||
memory_type = Column(String(50), nullable=False, index=True)
|
||||
title = Column(String(255), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
importance = Column(Integer, default=5, nullable=False)
|
||||
confidence = Column(Float, default=0.0, nullable=False)
|
||||
timeline_date = Column(DateTime, nullable=True)
|
||||
first_learned_at = Column(DateTime, default=utc_now, nullable=False)
|
||||
last_reinforced_at = Column(DateTime, nullable=True)
|
||||
reinforcement_count = Column(Integer, default=0, nullable=False)
|
||||
status = Column(String(20), default="active", nullable=False, index=True)
|
||||
origin_candidate_id = Column(String(36), ForeignKey("brain_candidates.id"), nullable=True)
|
||||
origin_source_types = Column(JSON, nullable=True)
|
||||
metadata_ = Column(JSON, nullable=True)
|
||||
|
||||
|
||||
class BrainTag(BaseModel):
|
||||
__tablename__ = "brain_tags"
|
||||
|
||||
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
|
||||
name = Column(String(100), nullable=False, index=True)
|
||||
category = Column(String(50), nullable=False)
|
||||
priority = Column(String(20), default="secondary", nullable=False, index=True)
|
||||
score = Column(Float, default=0.0, nullable=False)
|
||||
last_seen_at = Column(DateTime, nullable=True)
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Column, String, Text, DateTime, Index, Enum as SQLEnum
|
||||
from sqlalchemy import Column, String, Text, Integer, Index
|
||||
from app.models.base import BaseModel
|
||||
import enum
|
||||
|
||||
@@ -22,12 +22,20 @@ class Log(BaseModel):
|
||||
level = Column(String(20), default=LogLevel.INFO.value, index=True) # debug/info/warning/error
|
||||
type = Column(String(20), default=LogType.SYSTEM.value, index=True) # agent/system/chat
|
||||
user_id = Column(String(36), nullable=True, index=True) # 关联用户
|
||||
request_id = Column(String(64), nullable=True, index=True)
|
||||
route = Column(String(255), nullable=True, index=True)
|
||||
method = Column(String(16), nullable=True, index=True)
|
||||
status_code = Column(Integer, nullable=True, index=True)
|
||||
error_type = Column(String(100), nullable=True)
|
||||
operation = Column(String(100), nullable=True, index=True)
|
||||
message = Column(Text, nullable=False) # 日志内容
|
||||
details = Column(Text, nullable=True) # 详细信息(JSON)
|
||||
source = Column(String(100), nullable=True) # 来源模块
|
||||
duration_ms = Column(String(20), nullable=True) # 执行耗时
|
||||
duration_ms = Column(Integer, nullable=True) # 执行耗时
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_logs_type_level', 'type', 'level'),
|
||||
Index('idx_logs_created_at', 'created_at'),
|
||||
Index('idx_logs_request_id', 'request_id'),
|
||||
Index('idx_logs_operation_status', 'operation', 'status_code'),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user