Files
ewoooc/database/autoheal_models.py
OoO bc3f9cc61a
All checks were successful
CD Pipeline / deploy (push) Successful in 57s
補上 action_plans 寫入護欄
2026-05-12 23:35:25 +08:00

234 lines
8.8 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 CheckConstraint, 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__ = (
CheckConstraint(
"action_type IS NOT NULL OR created_by IS NOT NULL",
name="chk_action_plans_source_marker",
),
CheckConstraint(
"action_type IS NULL OR action_type IN ('auto', 'code_review_fix', 'openclaw_recommendation')",
name="chk_action_plans_action_type",
),
CheckConstraint(
"created_by IS NULL OR created_by IN ("
"'nemotron', 'openclaw', 'code_review_pipeline', "
"'ai_orchestrator', 'watcher_agent', 'agent_actions', "
"'elephant_alpha', 'manual', 'system'"
")",
name="chk_action_plans_created_by",
),
CheckConstraint(
"status IS NULL OR status IN ("
"'pending', 'approved', 'rejected', 'executed', "
"'auto_pending', 'auto_disabled', 'pending_review'"
")",
name="chk_action_plans_status",
),
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
}
]