Files
ewoooc/database/autoheal_models.py
OoO caa6263872
All checks were successful
CD Pipeline / deploy (push) Successful in 56s
同步 incidents 相容欄位寫入
2026-05-12 23:31:33 +08:00

211 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, ForeignKey, Index, Float
from sqlalchemy.orm import relationship
from database.models import Base
from datetime import datetime
class AgentContext(Base):
"""
共享上下文表(替代硬編碼鏈),支援多 Agent 存取與 TTL。
索引:(session_id, agent_name, context_key) 以加速跨 Agent 查詢。
"""
__tablename__ = 'agent_context'
id = Column(Integer, primary_key=True, autoincrement=True)
session_id = Column(String(64), nullable=False, index=True)
agent_name = Column(String(50), nullable=False, index=True)
context_key = Column(String(100), nullable=False)
context_val = Column(Text) # JSON string
created_at = Column(DateTime, default=datetime.now)
ttl_minutes = Column(Integer, default=60)
__table_args__ = (
Index('idx_agent_context_session_key', 'session_id', 'agent_name', 'context_key'),
Index('idx_agent_context_session_ttl', 'session_id', 'created_at'),
{'extend_existing': True},
)
class ActionPlan(Base):
"""
行動計畫表 — 統一 schema超集
Group A01-init.sql / CodeReview / OpenClaw:
action_type, description, priority, metadata_json
Group Bmigration 017 / watcher_agent / ai_orchestrator:
session_id, plan_type, sku, payload, created_by, approved_by, executed_at
migration 019 已在 DB 補齊所有欄位。
"""
__tablename__ = 'action_plans'
id = Column(Integer, primary_key=True, autoincrement=True)
# Group A columns
action_type = Column(String(100), nullable=True) # code_review_fix / openclaw_recommendation
description = Column(Text) # 人類可讀的行動說明
status = Column(String(50), default='pending') # pending/auto_pending/pending_review/executed
priority = Column(Integer, default=3) # 1=critical 2=high 3=medium 4=low
metadata_json = Column(Text) # JSON: pipeline_id/commit_sha/findings
created_at = Column(DateTime, default=datetime.now)
# Group B columns (ADR-012 / NemoTron)
session_id = Column(String(64), nullable=True)
plan_type = Column(String(50), nullable=True) # price_adjust / restock / campaign
sku = Column(String(100), nullable=True)
payload = Column(Text) # JSON 行動內容
created_by = Column(String(50)) # nemotron / openclaw
approved_by = Column(String(100), nullable=True) # Telegram user_id
executed_at = Column(DateTime, nullable=True)
__table_args__ = (
Index('idx_action_plans_type', 'action_type'),
Index('idx_action_plan_sku_status', 'sku', 'status'),
Index('idx_action_plan_created', 'created_at'),
{'extend_existing': True},
)
class ActionOutcome(Base):
"""
行動結果追蹤(閉環學習核心)。
"""
__tablename__ = 'action_outcomes'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True, autoincrement=True)
plan_id = Column(Integer, ForeignKey('action_plans.id'), nullable=False)
metric_type = Column(String(50), nullable=True) # sales_7d / price_rank / conversion
before_val = Column(Float)
after_val = Column(Float)
measured_at = Column(DateTime)
verdict = Column(String(20)) # effective / neutral / backfired
created_at = Column(DateTime, default=datetime.now)
plan = relationship("ActionPlan", backref="outcomes")
class AgentStrategyWeights(Base):
"""
Agent strategy weights (OpenClaw learning accumulation).
Index: strategy_key for fast updates/query.
"""
__tablename__ = 'agent_strategy_weights'
id = Column(Integer, primary_key=True, autoincrement=True)
strategy_key = Column(String(100), unique=True, nullable=False) # e.g. price_cut_when_gap_gt_5pct
weight = Column(Float, default=1.0)
success_cnt = Column(Integer, default=0)
fail_cnt = Column(Integer, default=0)
updated_at = Column(DateTime, default=datetime.now)
__table_args__ = (
Index('idx_strategy_key', 'strategy_key'),
{'extend_existing': True},
)
class Incident(Base):
"""
Incident tracking for AIOps auto-healing.
"""
__tablename__ = 'incidents'
id = Column(Integer, primary_key=True, autoincrement=True)
task_name = Column(String(100), nullable=False)
error_type = Column(String(50), nullable=False)
error_message = Column(Text, nullable=False)
error_traceback = Column(Text)
traceback_str = Column(Text)
severity = Column(String(20), default='medium')
status = Column(String(20), default='open') # open/healing/closed/escalated
retry_count = Column(Integer, default=0)
playbook_id = Column(Integer, ForeignKey('playbooks.id'), nullable=True)
matched_playbook_id = Column(Integer, ForeignKey('playbooks.id'), nullable=True)
resolved_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now)
# Relationship
playbook = relationship("Playbook", foreign_keys=[matched_playbook_id], backref="incidents")
class Playbook(Base):
"""
Playbook definitions for auto-healing actions.
"""
__tablename__ = 'playbooks'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
description = Column(Text)
error_type = Column(String(50), nullable=False)
match_pattern = Column(Text) # JSON array of patterns
action_type = Column(String(50), nullable=False) # DOCKER_RESTART/SSH_CMD/ALERT_ONLY/WAIT_RETRY
action_params = Column(Text) # JSON params
max_retries = Column(Integer, default=3)
cooldown_min = Column(Integer, default=30)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now)
def get_match_patterns(self):
"""Parse match_pattern JSON to list"""
if self.match_pattern:
try:
import json
return json.loads(self.match_pattern)
except:
return []
return []
def get_action_params(self):
"""Parse action_params JSON to dict"""
if self.action_params:
try:
import json
return json.loads(self.action_params)
except:
return {}
return {}
class HealLog(Base):
"""
Healing execution logs.
"""
__tablename__ = 'heal_logs'
id = Column(Integer, primary_key=True, autoincrement=True)
incident_id = Column(Integer, ForeignKey('incidents.id'), nullable=False)
playbook_id = Column(Integer, ForeignKey('playbooks.id'), nullable=False)
action_type = Column(String(50), nullable=False)
action_detail = Column(Text)
result = Column(String(20), default='unknown') # success/failed/skipped
result_output = Column(Text)
duration_ms = Column(Float)
created_at = Column(DateTime, default=datetime.now)
# Relationships
incident = relationship("Incident", backref="heal_logs")
playbook = relationship("Playbook", backref="heal_logs")
# Seed playbooks for common issues
SEED_PLAYBOOKS = [
{
'name': 'Database Connection Recovery',
'error_type': 'database',
'match_pattern': '["connection", "timeout", "unreachable"]',
'action_type': 'WAIT_RETRY',
'action_params': '{"wait_minutes": 5}',
'max_retries': 3,
'cooldown_min': 15
},
{
'name': 'Disk Space Alert',
'error_type': 'disk_space',
'match_pattern': '["disk", "space", "full", "no space"]',
'action_type': 'ALERT_ONLY',
'action_params': '{"message": "Disk space critical - manual intervention required"}',
'max_retries': 1,
'cooldown_min': 60
}
]