Files
ewoooc/database/autoheal_models.py
ogt aef8982cbb
All checks were successful
CD Pipeline / deploy (push) Successful in 1m16s
fix: add Incident/Playbook/HealLog to autoheal_models.py (was never committed)
ADR-013 AIOps classes Incident, Playbook, HealLog existed locally but were
missing from git. manager.py imports them → ImportError on every scheduler
restart. Also fixes transitive MetaData conflict with ai_models.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 04:50:28 +08:00

196 lines
6.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):
"""
行動計畫表NemoTron 輸出,等待審核與執行追蹤)。
"""
__tablename__ = 'action_plans'
id = Column(Integer, primary_key=True, autoincrement=True)
session_id = Column(String(64), nullable=True)
plan_type = Column(String(50), nullable=True) # price_adjust / restock / campaign
sku = Column(String(100), nullable=True, index=True)
payload = Column(Text) # JSON 行動內容
status = Column(String(20), default='pending') # pending/approved/rejected/executed
created_by = Column(String(50)) # nemotron / openclaw
approved_by = Column(String(100), nullable=True) # Telegram user_id
created_at = Column(DateTime, default=datetime.now)
executed_at = Column(DateTime, nullable=True)
__table_args__ = (
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)
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)
matched_playbook_id = Column(Integer, ForeignKey('playbooks.id'), nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now)
# Relationship
playbook = relationship("Playbook", 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
}
]